将 void * 转换为 char * 时出现段错误
segfault while converting void * to char *
#include <stdio.h>
typedef struct data{
char *s;
} data;
void func(void *a){
data p;
char *chr = (char *)a;
strcpy(p.s, (const char *)chr);
printf("%s\n", p.s);
}
int main(void)
{
func((void *)100); // segfault
return 0;
}
当我传递一些字符串值时,它会这样做,但一些数值会出现段错误。
我希望 func() 应该接受任何值,无论是整数还是字符串。
输出应该是:
100
您遇到分段错误是因为在您的函数中取消了对 100
指针的引用(隐含地,通过 printf("%s", a)
调用)。
具有 %s
参数(和 strcpy
)的 printf
函数尝试从给定位置的开头读取,直到到达 [=15=]
.
当您传递整数 100
时,您实际上指向 100 地址处的内存位置,使您的代码尝试从(可能)不可访问的位置读取。
如果你想用C做一个函数,自动识别传递的是字符串还是整数,有点困难。
我建议制作两个函数,一个用于打印整数,另一个用于打印字符串。它将使您的代码更简洁、更易于使用且错误更少。
(void *)100
将 100
转换为地址。稍后您访问该地址就好像在那里分配了一个字符串,除非这恰好是一些裸机微控制器应用程序,否则这是不太可能的。总体而言,该代码完全没有意义,原因是您 不能 通过反复试验进行编程 - 您实际上需要知道自己在做什么,不能猜测。
现在,您实际上在这里尝试做的似乎是编写一个可以处理字符串或整数的泛型函数。这应该以完全不同的方式完成。在旧式 C 中,您将使用枚举来指示 void*
指向的类型。在现代 C 语言中,我们可以借助包装宏来使用泛型特性:
#include <stdio.h>
void func_int (int x) { printf("%d\n", x); }
void func_str (const char* x) { puts(x); }
#define func(x) _Generic((x), int: func_int, char*: func_str)(x)
int main (void)
{
func(100);
func("hello");
}
void func(void *a){
data p;
char *chr = (char *)a;
p.s = chr;
printf("%s : %s\n", p.s, chr);
}
当您传递转换为指针的任意整数时,该数字必须在您访问(取消引用指针)该位置时转换为有效的内存位置。
如果指针未引用有效的内存位置,则行为是未定义并且任何事情都可能发生,包括段错误。
如果你想要打印多种类型的函数,你也需要传递类型。
typedef enum
{
STRING_TYPE,
SIGNED_INTEGER,
UNSIGNED_INTEGER,
FLOAT,
DOUBLE,
}partype;
void mysimpleprint(partype type, ...)
{
va_list va;
union{
int i;
unsigned u;
char *s;
float f;
double d;
}data;
va_start(va, type);
switch(type)
{
case STRING_TYPE:
data.s = va_arg(va, char *);
puts(data.s);
break;
case SIGNED_INTEGER:
data.i = va_arg(va, int);
printf("%d", data.i);
break;
case FLOAT:
data.f = va_arg(va, float);
printf("%f", data.i);
break;
default:
printf("Not implemented\n");
break;
}
}
#include <stdio.h>
typedef struct data{
char *s;
} data;
void func(void *a){
data p;
char *chr = (char *)a;
strcpy(p.s, (const char *)chr);
printf("%s\n", p.s);
}
int main(void)
{
func((void *)100); // segfault
return 0;
}
当我传递一些字符串值时,它会这样做,但一些数值会出现段错误。
我希望 func() 应该接受任何值,无论是整数还是字符串。
输出应该是:
100
您遇到分段错误是因为在您的函数中取消了对 100
指针的引用(隐含地,通过 printf("%s", a)
调用)。
具有 %s
参数(和 strcpy
)的 printf
函数尝试从给定位置的开头读取,直到到达 [=15=]
.
当您传递整数 100
时,您实际上指向 100 地址处的内存位置,使您的代码尝试从(可能)不可访问的位置读取。
如果你想用C做一个函数,自动识别传递的是字符串还是整数,有点困难。
我建议制作两个函数,一个用于打印整数,另一个用于打印字符串。它将使您的代码更简洁、更易于使用且错误更少。
(void *)100
将 100
转换为地址。稍后您访问该地址就好像在那里分配了一个字符串,除非这恰好是一些裸机微控制器应用程序,否则这是不太可能的。总体而言,该代码完全没有意义,原因是您 不能 通过反复试验进行编程 - 您实际上需要知道自己在做什么,不能猜测。
现在,您实际上在这里尝试做的似乎是编写一个可以处理字符串或整数的泛型函数。这应该以完全不同的方式完成。在旧式 C 中,您将使用枚举来指示 void*
指向的类型。在现代 C 语言中,我们可以借助包装宏来使用泛型特性:
#include <stdio.h>
void func_int (int x) { printf("%d\n", x); }
void func_str (const char* x) { puts(x); }
#define func(x) _Generic((x), int: func_int, char*: func_str)(x)
int main (void)
{
func(100);
func("hello");
}
void func(void *a){
data p;
char *chr = (char *)a;
p.s = chr;
printf("%s : %s\n", p.s, chr);
}
当您传递转换为指针的任意整数时,该数字必须在您访问(取消引用指针)该位置时转换为有效的内存位置。
如果指针未引用有效的内存位置,则行为是未定义并且任何事情都可能发生,包括段错误。
如果你想要打印多种类型的函数,你也需要传递类型。
typedef enum
{
STRING_TYPE,
SIGNED_INTEGER,
UNSIGNED_INTEGER,
FLOAT,
DOUBLE,
}partype;
void mysimpleprint(partype type, ...)
{
va_list va;
union{
int i;
unsigned u;
char *s;
float f;
double d;
}data;
va_start(va, type);
switch(type)
{
case STRING_TYPE:
data.s = va_arg(va, char *);
puts(data.s);
break;
case SIGNED_INTEGER:
data.i = va_arg(va, int);
printf("%d", data.i);
break;
case FLOAT:
data.f = va_arg(va, float);
printf("%f", data.i);
break;
default:
printf("Not implemented\n");
break;
}
}