六方云技術試験のまとめ

(1)問題文

関数の外部で宣言されたグローバル静的変数は、外部変数と呼ばれます。したがって、正解はcです。

(2)知識ポイント

  1. staticキーワード

(1)関数外のグローバル変数

変数が関数の外部で定義され、staticキーワードで修飾されている場合、その変数のスコープは定義されたソースファイル内に限定されます。他のソースファイルからはこの変数に直接アクセスできません。

(2)関数内のローカル変数

staticキーワードが関数内のローカル変数に使用される場合、その変数のライフサイクルはプログラムの実行全体に延長されます。通常の関数呼び出しサイクルではなく、変数は初めてそれを宣言した関数に入ったときにメモリが割り当てられ初期化され、後続の関数呼び出しでは変数の値が保持され、メモリの再割り当てと初期化は行われません。

この問題では、aは0-7、bは8-15、cは16-23の範囲を持ち、正解はcで3バイトです。

(1)ビットフィールド

ビットフィールドは、構造体内で指定された幅(ビット単位)のメンバを定義することで実現されます。

C言語におけるビットフィールド(Bit Fields)は、ユーザーがビットレベルのデータを定義および操作できる特別な構造体メンバです。ビットフィールドは主にメモリ効率の高いデータ保存とハードウェアレジスタへの直接操作に使用されます。各ビットフィールドメンバは、通常のバイト単位ではなく、構造体内の1つ以上のビットを占有することができます。

#include <stdio.h>

struct StatusFlags {
    unsigned int power : 1;     // 電源状態(1ビット)
    unsigned int mode : 2;      // モード(2ビット)
    unsigned int volume : 5;    // 音量(5ビット)
};

int main() {
    struct StatusFlags flags;
    
    flags.power = 1;     // 電源オン
    flags.mode = 2;      // モード2
    flags.volume = 15;   // 最大音量
    
    printf("Power: %d, Mode: %d, Volume: %d\n", flags.power, flags.mode, flags.volume);
    
    return 0;
}

(2)構造体のストレージ(メモリアラインメントの原則)

  1. 構造体のメンバ変数は、その基本データ型の長さの整数倍のメモリアドレスにのみ配置できます。

  2. 構造体のサイズは、最大のメンバ変数のデータ型の長さの整数倍でなければなりません。

  3. プログラムのスタック操作は後入れ先出し(LIFO)の原則に従います。

  4. C言語の演算子の優先順位と結合性

(1)優先順位

()→[]→.→->→単目演算子→双目演算子→三目演算子→カンマ演算子

特例:||(論理OR) > 三項演算子 > 代入演算子 > カンマ演算符

(2)結合性

大部分は左から右へ

単目演算子、三目演算子(?:)、代入演算子は右から左へ

  1. エンディアン(バイトオーダー)

(1)リトルエンディアン:低いアドレスに下位データビット、高いアドレスに上位データビットを格納

(2)ビッグエンディアン:低いアドレスに上位データビット、高いアドレスに下位データビットを格納

共用体(union)

int型変数xのアドレスがchar型変数yと共有される場合:

44 0x2000 0 33 0x2001 1 22 0x2002 2 11 0x2003 3

  1. 関数ポインタ

(1)定義

関数を指すポインタ

(2)関数ポインタの定義

例えば、整数を返し、2つの整数パラメータを受け取る関数を指すポインタを定義する場合:

int calculate(int x, int y) {
    return x * y;
}
int (*operation)(int, int);
  1. 配列tableが与えられた場合、マクロを定義して要素数を求める方法
#include <stdio.h>

#define DATA {10, 20, 30, 40, 50}
#define COUNT_OF(arr) (sizeof(arr) / sizeof((arr)[0]))

int main(void)
{
    int data[] = DATA;
    int elements = COUNT_OF(data);
    
    return 0;
}
  1. staticの用途

(1)静的グローバル変数:グローバル変数のスコープを制限し、宣言されたファイル内でのみアクセス可能にする

(2)静的ローカル変数:ローカル変数のライフサイクルを延長し、関数の複数回の呼び出し間で値を保持させる

(3)静的関数:関数のスコープを制限し、宣言されたファイル内でのみアクセス可能にする

  1. 配列とリンクリストの違い

(1)メモリ割り当てと空間アクセス

  1. 配列

配列のメモリ割り当ては連続的であり、要素にアクセスするには配列の添字を使用するだけです。

  1. リンクリスト

リンクリストのメモリ割り当ては動的で非連続的であり、要素にアクセスするにはリンクリストをポインタで走査する必要があります。

(2)データの挿入と削除

  1. 配列

配列でデータを削除または挿入する場合、配列の連続性を維持するために他の要素を移動する必要があり、面倒です。

  1. リンクリスト

リンクリストでの要素の挿入と削除は、他の要素を移動する必要がなく、ポインタでノードを操作するだけです。

(3)適用シナリオ

  1. 配列

要素への頻繁なアクセスが必要なシナリオに適しています。

  1. リンクリスト

頻繁な挿入と削除が必要なシナリオに適しています。

(4)実装の複雑さ

  1. 配列

簡単な作成で済みます。

  1. リンクリスト

リンクリストのヘッダ、各ノードの構造体、ポインタ操作を作成する必要があります。

3行目のコードの解析:ptr++は後置インクリメントなので、使用してから値が増加します。

*(ptr++) += 100;

*ptr = *ptr + 100;

ptr++;

ポインタstrを操作して指す先を変更するには、ポインタへのポインタ(二重ポインタ)を渡す必要があります。

void AllocateMemory(char **p)
{    
    *p = (char *)malloc(100);
}

void Test(void)
{
    char *str = NULL;
    AllocateMemory(&str);
}

メイン関数のaは単一のchar型文字変数であり、長い文字列のコピーはできません。

#include <stdio.h>
#include <string.h>

int main(void)
{
    char buffer[32] = {0};
    char *text = buffer;

    strcpy(text, "こんにちは");

    printf("%s\n", text);
}

unsigned char ucCmdNum;では、ucCmdNumの値の範囲は0〜255です。

この問題では範囲を超えており、無限ループに陥ります。

この問題では、memcpyのソースコードの理解が問われ、関数の仮引数と戻り値の両方がvoid型です。

void *custom_memcpy(void *destination, const void *source, size_t length)
{
    unsigned char *dest = (unsigned char *)destination;
    const unsigned char *src = (const unsigned char *)source;
    size_t i = 0;

    for (i = 0; i < length; i++)
    {
        dest[i] = src[i];
    }

    return destination;
}

memcpyの場合、コピーするメモリ領域が近接しているとメモリアドレスが部分的に重なる可能性があり、memmoveはこの問題を解決できます。

int string_compare(char *src, char *dest)
{
    while (*src == *dest && *src != '\0')
    {
        src++;
        dest++;
    }

    return *src - *dest;
}
  1. 片方向リンクリストのバブルソートの実例(ヘッダなしリンクリスト)
int LinkedListBubbleSort(LinkedList *list)
{
    Node *current = NULL;
    Node *next_node = NULL;
    Node *tail = NULL;
    Data temp = 0;
    
    current = list->head;
    if (NULL == current || NULL == current->next)//リストが空か要素が1つの場合のチェック
	{
		return -1;
	}

	while (1)
	{
		current = list->head;
		next_node = current->next;
		if (tail == next_node)//ソート完了の条件
		{
			break;
		}

		while (next_node != tail)
		{
			if (current->data > next_node->data)
			{
				temp = current->data;
				current->data = next_node->data;
				next_node->data = temp;
			}
			current = current->next;
			next_node = next_node->next;
		}
		tail = next_node;
	}

    return 0;
}

タグ: C言語 ビットフィールド 関数ポインタ メモリ管理 データ構造

6月6日 19:27 投稿