buffer = realloc(buffer, sizeof(uint32_t));
sprintf(buffer, "%d", gid);
offset += sizeof(uint32_t);
buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer));
sprintf(buffer+sizeof(uint32_t), "%d", uid);
これは実際には意味がなく、幸運な状況を除いて意図したとおりに機能しません。
CHAR_BIT == 8
通常が成り立つと仮定しましょうsizeof(uint32_t) == 4
。int
さらに、それがビットのパディングなしの2の補数表現の符号付き32ビット整数であると仮定します。
sprintf(buffer, "%d", gid)
toバッファgid
として解釈されるビットパターンの10進文字列表現を出力します。int
上記の仮定の下では、gid
は-2147483648から2147483647までの数値として解釈されます。したがって、10進文字列表現には、が含まれる場合があり'-'
、1〜10桁が含まれ、0ターミネータが含まれ、全体で2〜12バイトが使用されます。ただし、割り当てたバイトは4バイトしかないため、999 < gid < 2^32-99
(符号付き2の補数の解釈が> 999
または< -99
)の場合は常にsprintf
、割り当てられたバッファーサイズを超えて書き込みます。
それは未定義の振る舞いです。
通常、4バイトを割り当てると、メモリのチャンクが効果的に大きくなるため、すぐにはクラッシュしない可能性があります(たとえばmalloc
、常に16バイトの整列ブロックを返す場合、割り当てられた4のすぐ後ろの12バイトは、プログラムの他の部分では使用できませんが、プログラムのアドレス空間に書き込み、それらへの書き込みはおそらく検出されないでしょう)。ただし、割り当てられたチャンクの終わりがページ境界にある場合、後で簡単にクラッシュする可能性があります。
また、後続のsに対して書き込みオフセットを4バイト進めるためsprintf
、文字列表現(0ターミネータを除く)が4バイトを超えて使用された場合(書き込みのためにプログラムがまだクラッシュしなかった場合)、前の数値の一部が上書きされます。割り当てられていないメモリへ)。
この線
buffer = realloc(buffer, sizeof(uint32_t) + sizeof(buffer));
さらにエラーが含まれています。
buffer = realloc(buffer, new_size);
割り当てられたメモリへの参照を失い、realloc
失敗するとリークが発生します。一時的なものを使用して、成功を確認します
char *temp = realloc(buffer, new_size);
if (temp == NULL) {
/* reallocation failed, recover or cleanup */
free(buffer);
exit(EXIT_FAILURE);
}
/* it worked */
buffer = temp;
/* temp = NULL; or let temp go out of scope */
新しい割り当ての新しいサイズsizeof(uint32_t) + sizeof(buffer)
は常に同じですsizeof(uint32_t) + sizeof(char*)
。これは通常8バイトまたは12バイトであるため、割り当てられた領域の外に書き込むのに多くの数値を必要とせず、クラッシュまたはメモリの破損を引き起こします(これにより、かなり後でクラッシュが発生する可能性があります)。
割り当てられたバイト数を追跡し、それを使用して新しいサイズを計算する必要があります。buffer
ポインタからその開始までに割り当てられたメモリブロックのサイズを決定する(ポータブル¹)方法はありません。
ここで問題となるのは、文字列表現とビットパターンのどちらをバッファに格納するかです。
文字列表現を保存すると、文字列表現の長さが値によって異なるという問題があります。したがって、数値の表現の間に区切り文字を含めるか、必要に応じて(スペースまたは先行ゼロを使用して)パディングすることにより、すべての表現が同じ長さになるようにする必要があります。たとえば、次のように機能します
#include <stdint.h>
#include <inttypes.h>
#define MAKESTR(x) # x
#define STR(x) MAKESTR(x)
/* A uint32_t can use 10 decimal digits, so let each field be 10 chars wide */
#define FIELD_WIDTH 10
uint32_t gid = 1100;
uint32_t uid = 1000;
size_t buf_size = 0, offset = 0;
char *buffer = NULL, *temp = NULL;
buffer = realloc(buffer, FIELD_WIDTH + 1); /* one for the '\0' */
if (buffer == NULL) {
exit(EXIT_FAILURE);
}
buf_size = FIELD_WIDTH + 1;
sprintf(buffer, "%0" STR(FIELD_WIDTH) PRIu32, gid);
offset += FIELD_WIDTH;
temp = realloc(buffer, buf_size + FIELD_WIDTH);
if (temp == NULL) {
free(buffer);
exit(EXIT_FAILURE);
}
buffer = temp;
temp = NULL;
buf_size += FIELD_WIDTH;
sprintf(buffer + offset, "%0" STR(FIELD_WIDTH) PRIu32, uid);
offset += FIELD_WIDTH;
/* more */
uint32_t valorGID;
uint32_t valorUID;
/* rewind for scanning */
offset = 0;
sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorGID);
offset += FIELD_WIDTH;
sscanf(buffer + offset, "%" STR(FIELD_WIDTH) SCNu32, &valorUID);
printf("ValorGID %u ValorUID %u \n", valorGID, valorUID);
ゼロが埋め込まれた固定幅フィールドを使用します。固定幅ではなくセパレーターを使用する場合、必要な長さとオフセットの計算はより複雑になりますが、数値が大きくない限り、使用するスペースは少なくなります。
最もコンパクトな保存方法であるビットパターンを保存したい場合は、次のようなものを使用します。
size_t buf_size = 0, offset = 0;
unsigned char *buffer = NULL, temp = NULL;
buffer = realloc(buffer, sizeof(uint32_t));
if (buffer == NULL) {
exit(EXIT_FAILURE);
}
buf_size = sizeof(uint32_t);
for(size_t b = 0; b < sizeof(uint32_t); ++b) {
buffer[offset + b] = (gid >> b*8) & 0xFF;
}
offset += sizeof(uint32_t);
temp = realloc(buffer, buf_size + sizeof(uint32_t));
if (temp == NULL) {
free(buffer);
exit(EXIT_FAILURE);
}
buffer = temp;
temp = NULL;
buf_size += sizeof(uint32_t);
for(size_t b = 0; b < sizeof(uint32_t); ++b) {
buffer[offset + b] = (uid >> b*8) & 0xFF;
}
offset += sizeof(uint32_t);
/* And for reading the values */
uint32_t valorGID, valorUID;
/* rewind */
offset = 0;
valorGID = 0;
for(size_t b = 0; b < sizeof(uint32_t); ++b) {
valorGID |= buffer[offset + b] << b*8;
}
offset += sizeof(uint32_t);
valorUID = 0;
for(size_t b = 0; b < sizeof(uint32_t); ++b) {
valorUID |= buffer[offset + b] << b*8;
}
offset += sizeof(uint32_t);
¹実装での作業などがわかっている場合は、の簿記データmalloc
からサイズを見つけることができる場合があります。malloc