C中未初始化的指针在MinGW中工作得出人意料
我的朋友给我发了这段代码,说如果我能找到任何问题,我检查了代码,发现他使用的是未初始化的指针C中未初始化的指针在MinGW中工作得出人意料,c,pointers,C,Pointers,我的朋友给我发了这段代码,说如果我能找到任何问题,我检查了代码,发现他使用的是未初始化的指针int*ptr并告诉他,但他说这段代码有效,所以我复制了代码并用gcc编译了它,它在执行时给了我分段错误(内核转储),但他声称这段代码有效,并给了我一个屏幕截图(他在Dev-C++中使用MinGW) 所以问题是,当使用未初始化的指针时,为什么这段代码在MinGW中工作 #include<stdio.h> int main() { int size,i,j,sum=0; puts
int*ptr
并告诉他,但他说这段代码有效,所以我复制了代码并用gcc编译了它,它在执行时给了我分段错误(内核转储),但他声称这段代码有效,并给了我一个屏幕截图(他在Dev-C++中使用MinGW)
所以问题是,当使用未初始化的指针时,为什么这段代码在MinGW中工作
#include<stdio.h>
int main() {
int size,i,j,sum=0;
puts("enter1");
scanf("%d",&size);
int arr1[size];
puts("enter1");
int *ptr;
for(i=0;i<size;i++) {
scanf("%d",(ptr+i));
}
for(i=0;i<size;i++) {
sum=sum+*(ptr+i);
}
printf("'%d'",sum);
return 0;
}
#包括
int main(){
整数大小,i,j,和=0;
认沽权(“1”);
scanf(“%d”,大小(&S);
int arr1[大小];
认沽权(“1”);
int*ptr;
对于(i=0;i取消对未初始化指针的引用是未定义的行为。正如您和您的朋友所看到的,有时这个“有效”,有时它会出现故障,而在其他一些机器上,它可能会让恶魔飞出您的鼻子(甚至在同一台机器上,它可能会在不同的运行中发生变化)。它是未定义的,结果可能是任何东西。因为“它在他的机器上工作”(很可能是堆栈使用模式的巧合,在堆栈上留下了一个半有效的内存地址供他使用)并不正确。“未定义”并不意味着“非法”“。这只是意味着几乎任何结果都是可能的,包括看起来工作正常。如果未初始化的指针值对应于当前未使用的可写地址,或者如果缓冲区溢出溢出到未使用的内存中,则代码将愉快地运行,不会出现任何外部问题
这是UB最有害的表现形式,正是因为它看起来工作正常。它将继续正常工作,直到运行时环境发生变化,或者你在代码中做了一个不相关的更改并重新构建,然后突然所有的麻烦都消失了,你不知道为什么。我不止一次遇到过这种情况。在启用警告的情况下,使用gcc
编译时,编译器输出:
gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled2.c" -o "untitled2.o"
untitled2.c: In function ‘main’:
untitled2.c:6:9: warning: unused variable ‘arr1’ [-Wunused-variable]
6 | int arr1[size];
| ^~~~
untitled2.c:3:16: warning: unused variable ‘j’ [-Wunused-variable]
3 | int size,i,j,sum=0;
| ^
untitled2.c:10:9: warning: ‘ptr’ may be used uninitialized in this function [-Wmaybe-uninitialized]
10 | scanf("%d",(ptr+i));
| ^~~~~~~~~~~~~~~~~~~
Compilation finished successfully.
因此,必须启用警告才能从编译器获取任何有用的消息
关于:
Compilation finished successfully.
当出现警告消息时,此输出仅表示编译器对代码应用了一些变通方法,而不是生成了正确的代码
此外,关于:
int *ptr;
这将在运行时导致seg故障事件,因为它不指向程序拥有的内存
在尝试实际运行可执行文件之前,应修复上述所有问题
建议的守则如下:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
//int size,i,j,sum=0;
int size;
int sum=0;
//puts( "enter1" );
puts( "enter number integers to be summed" );
if( scanf("%d",&size) != 1 )
{
fprintf( stderr, "scanf for number of integers to sum failed\n" );
exit( EXIT_FAILURE );
}
//int arr1[size];
puts("enter the integers, seperated by a space or newline");
//int *ptr;
int inputData[ size ];
for( int i=0; i<size; i++ )
{
//scanf("%d",(ptr+i));
if( scanf( "%d", inputData + i ) != 1 )
{
fprintf( stderr, "scanf for int data failed\n" );
exit( EXIT_FAILURE );
}
}
for( int i=0; i<size; i++ )
{
sum += *( inputData+i );
}
printf( "'%d'", sum );
return 0;
}
干净地编译
执行所需的功能
检查并处理I/O错误
现在,拟议的守则:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
//int size,i,j,sum=0;
int size;
int sum=0;
//puts( "enter1" );
puts( "enter number integers to be summed" );
if( scanf("%d",&size) != 1 )
{
fprintf( stderr, "scanf for number of integers to sum failed\n" );
exit( EXIT_FAILURE );
}
//int arr1[size];
puts("enter the integers, seperated by a space or newline");
//int *ptr;
int inputData[ size ];
for( int i=0; i<size; i++ )
{
//scanf("%d",(ptr+i));
if( scanf( "%d", inputData + i ) != 1 )
{
fprintf( stderr, "scanf for int data failed\n" );
exit( EXIT_FAILURE );
}
}
for( int i=0; i<size; i++ )
{
sum += *( inputData+i );
}
printf( "'%d'", sum );
return 0;
}
#包括
#包括
内部主(空)
{
//整数大小,i,j,和=0;
整数大小;
整数和=0;
//认沽权(“1”);
puts(“输入要求和的整数”);
如果(扫描频率(“%d”,&size)!=1)
{
fprintf(stderr,“scanf用于求和失败的整数数\n”);
退出(退出失败);
}
//int arr1[大小];
puts(“输入整数,用空格或换行符分隔”);
//int*ptr;
int输入数据[大小];
对于(int i=0;当调用未定义的行为似乎起作用时,请不要感到“惊讶”。毕竟,该行为是未定义的。无论谁/无论什么人告诉您调用UB保证不起作用,他们都大错特错。未定义意味着没有定义,因此您确实无法对它将起什么作用(或不起作用)做出任何结论。因此,任务是通过严格遵守已定义的行为来编写毫无疑问的代码。好的,我会记住这一点。显然,它只是碰巧指向了一些您可以读写的有效地址。您的程序只是重写了一些它不关心的变量。如果您运气不好,您的程序将使用一些变量ram确实很关心,这会导致崩溃。“当使用未初始化的指针时,应该是UB??嗯,在他的情况下,它起作用是未定义的行为。