C语言结构体函数指针赋值类型不匹配导致段错误及打印异常
我编写了如下程序,用于学习函数指针及其在结构体中的用法:
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <math.h> typedef struct S_t S_t; struct S_t { float *s_ptr; uint32_t ns; }; typedef struct p_t p_t; struct p_t { int32_t pID; float pVal; }; typedef struct pr_t pr_t; struct pr_t { S_t *S; int (*TrimS)(S_t *TS, int sSize); p_t *TP; }; int ChopS(S_t *TS, int size); float fvals[] = {0.8000000119209290, 0.2399999946355820, -0.3740000128746033, 0.2489999979734421, 0.9049999713897705, 0.3770000040531158, -0.6959999799728394, -0.8870000243186951, -0.9739999771118164, 0.1410000026226044, 0.4040000140666962, 0.7779999971389771}; int arrLen = sizeof(fvals) / sizeof(fvals[0]); float svals[20]; int ChopS(S_t *TS, int size) { int i, c; for(i = 0; i < TS->ns; i++) { if (i >= size) TS->s_ptr[i] = 0.0; } c = 0; for(i = 0; i < TS->ns; i++) { if(TS->s_ptr[i] == 0.0) c++; } if (c == size) return 0; else return -1; } int main() { int i, n; p_t p1 = {.pID = 1234, .pVal = 0.35}; p_t *p2 = NULL; S_t tS1; S_t *tS2 = NULL; tS2 = malloc(sizeof(S_t)); pr_t SP1; pr_t *SP2 = NULL; SP2 = malloc(sizeof(pr_t)); p2 = (p_t *) malloc(sizeof(p_t)); p2->pID = 1111; p2->pVal = 1.198; printf("Generating new values from original inputs as float array : \n"); for(i = 0; i < n; i++) { svals[i] = 1 + fvals[i]; printf("%f, ", svals[i]); } printf("\n"); tS1.s_ptr = fvals; tS1.ns = arrLen; tS2->s_ptr = svals; tS2->ns = arrLen; SP1.S = &tS1; SP1.TP = &p1; SP1.TrimS = ChopS(&tS1, 4); printf("Original inputs from PR1 (Trim=%d) as %d inputs : \n", SP1.TrimS, SP1.S->ns); for(i = 0; i < n; i++) { printf("%f, ", SP1.S->s_ptr[i]); } printf("\n"); SP2->S = tS2; SP2->TP = p2; SP2->TrimS = ChopS(&tS2, 8); printf("Modified inputs from PR2 (Trim=%d) as %d inputs : \n", SP2->TrimS, SP2->S->ns); for(i = 0; i < n; i++) { printf("%f, ", SP2->S->s_ptr[i]); } printf("\n"); return 0; }
初始编译警告
使用gcc编译时出现如下警告:
user@PCLT:~/Test$ gcc -std=c99 -Wall -o executetest exp.c exp.c: In function ‘main’: exp.c:95:15: warning: assignment makes pointer from integer without a cast [-Wint-conversion] SP1.TrimS = ChopS(&tS1, 4); ^ exp.c:96:45: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int (*)(S_t *, int) {aka int (*)(struct S_t *, int)}’ [-Wformat=] printf("Original inputs from PR1 (Trim=%d) as %d inputs : \n", ~^ *(SP1.TrimS), SP1.S->ns); ~ exp.c:106:24: warning: passing argument 1 of ‘ChopS’ from incompatible pointer type [-Wincompatible-pointer-types] SP2->TrimS = ChopS(&tS2, 8); ^ exp.c:39:5: note: expected ‘S_t * {aka struct S_t *}’ but argument is of type ‘S_t ** {aka struct S_t **}’ int ChopS(S_t *TS, int size) ^~~~~ exp.c:106:16: warning: assignment makes pointer from integer without a cast [-Wint-conversion] SP2->TrimS = ChopS(&tS2, 8); ^ exp.c:107:45: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int (*)(S_t *, int) {aka int (*)(struct S_t *, int)}’ [-Wformat=] printf("Modified inputs from PR2 (Trim=%d) as %d inputs : \n", ~^ *(SP2->TrimS), SP2->S->ns); ~
初始运行异常
运行编译出的可执行文件后输出如下,触发段错误:
user@PCLT:~/Test$ ./executetest Generating new values from original inputs as float array : 1.800000, 1.240000, 0.626000, 1.249000, 1.905000, 1.377000, 0.304000, 0.113000, 0.026000, 1.141000, 1.404000, 1.778000, Original inputs from PR1 (Trim=-1) as 12 inputs : 0.800000, 0.240000, -0.374000, 0.249000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, Segmentation fault (core dumped)
其中非指针类型的pr_t变量SP1运行结果符合预期,但指针类型的pr_t变量SP2运行异常。
首次修改后的新问题
根据建议修改代码:
- 将原代码第95行替换为:
SP1.TrimS = ChopS; SP1.TrimS(&tS1, 4);
- 将原代码第106行替换为:
SP2->TrimS = ChopS; SP2->TrimS(tS2, 8);
修改后程序不再触发段错误,但打印Trim值时输出为(Trim=94135985899402)这类大数字,不符合预期的(Trim=0)状态码。
错误原因说明
代码中一共存在4个核心问题:
函数指针赋值逻辑错误
TrimS是函数指针类型,存储的是函数的入口地址。直接写ChopS(&tS1, 4)是调用ChopS函数,会把函数返回的int类型结果赋值给函数指针,触发类型不匹配警告。正确的赋值方式是直接传入函数名:SP1.TrimS = ChopS;,需要执行裁剪逻辑时再通过函数指针传参调用。参数类型不匹配
tS2本身就是S_t*类型,传入&tS2会得到S_t**类型的二级指针,和ChopS函数要求的S_t*参数类型不符,触发指针类型不兼容警告。调用时直接传入tS2即可。未初始化变量导致越界
代码中定义了循环变量n但从未赋值,n是栈上的随机值。如果n的值大于数组实际长度,循环访问数组时就会越界访问非法内存,这是段错误的核心原因。这里n应该在循环前赋值为arrLen。打印对象错误导致输出异常数字
修改后直接打印SP1.TrimS,实际打印的是函数指针存储的函数入口内存地址,所以会输出无意义的大数字。如果需要获取裁剪操作的返回状态码,需要定义int类型变量接收函数调用的返回值,再打印这个变量:
int trim_ret1 = SP1.TrimS(&tS1, 4); // 后续printf中打印trim_ret1即可
内容的提问来源于stack exchange,提问作者skrowten_hermit

