如何解决C语言段错误?替换sizeof值触发问题求助
段错误排查与修复需求
我知道Segmentation fault(段错误)通常因写入只读内存或未分配内存导致,添加大量printf调试后仍未找到原因。以下是怀疑引发段错的代码片段:
static bool init_rand(void *data, size_t size) { FILE *stream = fopen("/dev/urandom", "r"); if (stream == NULL) return false; bool ok = (fread(data, sizeof(uint8_t), size, stream) == size); for (int i = 0; i < size; i++) { printf("%02x", ((unsigned char *) data) [i]); } fclose(stream); return ok; } union uint256_s { uint8_t i8[32]; uint16_t i16[16]; uint32_t i32[8]; uint64_t i64[4]; }; typedef union uint256_s uint256_t; static inline uint256_t sha256(const void *data, size_t len) { secp256k1_sha256_t cxt; secp256k1_sha256_initialize(&cxt); secp256k1_sha256_write(&cxt, (uint8_t *)data, (int)len); uint256_t res; secp256k1_sha256_finalize(&cxt, (uint8_t *)&res); return res; } static uint256_t sha256d(const void *data, size_t len) { uint256_t hsh = sha256(data, len); hsh = sha256(&hsh, sizeof(hsh)); return hsh; } struct starp { uint256_t starp; uint128_t counter; }; static struct starp *make_starp(void) { struct starp *starp = (struct starp *)malloc(sizeof(struct starp)); assert(starp != NULL); starp->counter = 0; if (!init_rand(starp, sizeof(struct starp))) { fprintf(stderr, "error: failed to init random starp\n"); exit(EXIT_FAILURE); } if (starp->counter == 0) // Sanity check... { fprintf(stderr, "error: random starp initialization failed\n"); exit(EXIT_FAILURE); } return starp; } static uint256_t rand256(struct starp *starp) { starp->counter++; return sha256(starp,sizeof(struct starp)); } static uint160_t gen_hash160(const uint8_t *pub_key) { uint160_t hsh160 = hash160(pub_key); return hsh160; } #define BASE_MAX 0xFFFF static secp256k1_gej_t bases[BASE_MAX]; static secp256k1_scalar_t priv_bases[BASE_MAX]; #define OFFSET_MAX_COL 9 #define OFFSET_MAX_ROW BASE_MAX #define BITS 16 #define NUM_PARTS(n) (((n) - 1) / 16 + 1) #define NUM_INIT_WORKERS 4 static secp256k1_ge_t offsets[OFFSET_MAX_ROW][OFFSET_MAX_COL]; static secp256k1_scalar_t priv_offsets[OFFSET_MAX_ROW][OFFSET_MAX_COL]; static bool read_pub_table(FILE *stream); static bool write_pub_table(FILE *stream); static bool read_priv_table(FILE *stream); static bool write_priv_table(FILE *stream); static void *init_worker(void *arg) { struct starp *starp = make_starp(); size_t i = (size_t)arg; for (size_t j = 0; j < OFFSET_MAX_ROW; j++) { int overflow = 0; do { uint256_t x = rand256(starp); secp256k1_scalar_set_b32(&priv_offsets[j][i], x.i8, &overflow); } while (overflow); secp256k1_gej_t tmp; secp256k1_ecmult_gen(&cxt->ecmult_gen_ctx, &tmp, &priv_offsets[j][i]); secp256k1_ge_set_gej(&offsets[j][i], &tmp); } free(starp); putchar('.'); printf(" Init Worker executes\n"); fflush(stdout); return NULL; }
当把所有sizeof(struct starp)替换为12时触发段错误,替换为48则正常。我需要程序读取12字节而非32字节私钥,求解决该段错问题。
问题根因与修复方案
核心问题
struct starp的实际内存大小是48字节(uint256_t占32字节,uint128_t占16字节),强行将操作长度替换为12会导致:
init_rand仅初始化结构体前12字节,starp->counter的剩余字节为未定义垃圾值,后续的counter == 0检查逻辑完全失效。rand256中sha256只读取12字节,但结构体实际占用48字节,读取范围不完整会引发内存越界或逻辑错误,最终触发段错。- 需求是“读取12字节私钥”,但错误修改了整个结构体的操作长度,完全偏离目标。
修复步骤
1. 调整结构体定义,明确12字节随机种子
把原结构体中的32字节uint256_t改为12字节数组,直接对应需求:
struct starp { uint8_t starp_seed[12]; // 12字节随机种子 uint128_t counter; };
2. 修改相关函数的操作范围
make_starp中修正初始化范围:只初始化12字节的种子部分,去掉无意义的counter检查(因为counter初始化为0后会自增,不需要随机初始化):
static struct starp *make_starp(void) { struct starp *starp = (struct starp *)malloc(sizeof(struct starp)); assert(starp != NULL); starp->counter = 0; // 仅初始化12字节的种子 if (!init_rand(starp->starp_seed, sizeof(starp->starp_seed))) { fprintf(stderr, "error: failed to init random starp\n"); exit(EXIT_FAILURE); } return starp; }
rand256中读取完整结构体:确保sha256读取整个结构体的有效内存,避免越界:
static uint256_t rand256(struct starp *starp) { starp->counter++; // 读取完整结构体大小,保证内存访问合法 return sha256(starp, sizeof(struct starp)); }
3. 适配secp256k1私钥要求
secp256k1_scalar_set_b32要求输入是32字节的合法标量(不超过曲线阶)。如果用12字节生成私钥,需要将12字节扩展为32字节(比如补0或做哈希),否则overflow会持续触发,导致死循环:
// 示例:将12字节种子哈希为32字节后再作为标量 uint256_t seed_hash = sha256(starp->starp_seed, sizeof(starp->starp_seed)); secp256k1_scalar_set_b32(&priv_offsets[j][i], seed_hash.i8, &overflow);
调试建议
- 用
gdb定位段错:运行gdb ./你的程序,崩溃后输入bt查看调用栈,直接找到崩溃代码行。 - 开启编译警告:添加
-Wall -Wextra编译选项,提前发现内存越界、类型不匹配等潜在问题。
内容的提问来源于stack exchange,提问作者terry franklin
相关产品推荐
相关产品推荐

