据我所知,在C中,有一些数组可以在声明时给出一个长度。我想知道这些长度声明是否仅供其他程序员查看和理解使用,或者是否可以通过禁止读取超过缓冲区长度的字符来保护代码。当我读入一个字符串时,它只是继续前进,并开始覆盖存储在我想要读入的缓冲区之后声明的变量中的数据。是否有安全的方式来读取数据?
char arr[5];
char buff[5] = "cat";
printf("The buffer holds: %s\n", buff);
printf("Input a word to be held in \"arr\": ");
scanf("%s", arr);
printf("The array holds: %s\n", arr);
printf("The buffer holds: %s\n", buff);
printf("%c\n", arr[9]);
如果读入arr的字符串足够长,则会覆盖“cat”,并且没有任何编译标志似乎做任何事情(我使用-Wextra -Wall -Werror -std = c99编译)唯一抱怨的是Valgrind的。如何在C中编写安全数组代码?
答案 0 :(得分:3)
从某种意义上说,C语言本身既不保护你也不保护你不会超出数组的范围。更准确地说,C编译器不需要执行边界检查,但允许这样做。 (很少有编译器利用该权限。非常默认情况下很少这样做。)
例如,如果你写:
int arr[10];
arr[20] = 42;
行为未定义。这并不意味着你的程序会崩溃。它并不意味着错误将或将检测到。引用ISO C标准,
行为,使用不可移植或错误的程序构造或 错误的数据,本国际标准没有规定 要求
典型的C编译器可能会生成采用arr
基址的代码,向其添加20 * sizeof (int)
的偏移量,然后尝试将42
存储在结果位置。如果没有显式或隐式检查,这可能会破坏其他一些数据结构,它可能会写入您的进程所拥有的内存,但不会用于其他任何内容,或者它可以终止您的程序。 (或者#include <stdjoke.h>
它可以让恶魔飞出你的鼻子。)
但是符合标准的C编译器可以添加代码以检查索引是否在0到9的范围内,并且如果它不是,则采取一些明智的操作。 C不禁止边界检查;它只是不需要它。
在这种特殊情况下,可以(但不是必须)在编译时检测到数组访问超出范围,因此编译器可以发出编译时警告。 (如果在运行时直到知道索引值,这是不可能的。)
最终,避免越界访问的责任落在你这个程序员身上。不要以为编译器会为你检查它 - 并且不要认为它不会。
答案 1 :(得分:1)
C遵循&#34的理念;程序员最了解&#34;并且&#34;我不能牵着你的手#34;
这就是为什么C如此之快,它不必进行任何检查。
为了安全的用户输入,您可以使用fgets
类似于:
fgets(arr, sizeof(arr), stdin);
arr
将输入保持为指定的大小。有关详细信息,我建议使用fgets的手册页
http://linux.die.net/man/3/fgets
您可能需要对此进行多次调用才能从stdin获取所有输入。
答案 2 :(得分:0)
C不会保护您不要越过数组的末尾。有办法检测它。看这篇文章
Setting up a bounds-protected array
试试此代码
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ARRAY_SIZE 100
int main(void) {
size_t i = 0;
char arr1[ARRAY_SIZE];
char * arr2 = malloc(ARRAY_SIZE );
for(i = 0; i < 200; i++) {
arr1[i] = '1';
arr2[i] = '2';
}
for(i = 0; i < 200; i++) {
printf("%zu arr1[i]=%c \n", i, arr1[i]);
printf("%zu arr2[i]=%c \n", i, arr2[i]);
}
return 0;
}
使用以下编译时选项(这仅适用于gcc,即clang不会出错)
gcc -O3 -Wall -std=c11 -pedantic array_overflow_at_03.c
然后使用
尝试gcc -Wall -std=c11 -pedantic array_overflow_at_03.c
执行此操作的每种方法都有其优点,您的应用程序需求将确定使用哪种方法。
答案 3 :(得分:0)
C中的数组大小仅告诉编译器为阵列保留多少内存。 C不会插入代码来检查是否超出了数组边界。大小&#39; 5&#39;在int a[5];
中没有存储在已编译的程序中。它只在源代码中。其他能看到源代码的程序员可以看到它;没有人可以。
由于C没有检查你做了什么并握住你的手(见Lyle Rolleman的答案),C不会&#34;#34;检测&#34;缓冲区溢出。因此,当发生这种情况时,行为是未定义的(所谓的&#34;未定义的行为&#34;或UB)。经常发生的是堆栈被覆盖,堆栈上是返回地址给调用者。这被覆盖,当前函数想要返回时,它会跳到&#34;无处&#34; (或者某个地方,因为这种行为被&#34;堆栈漏洞使用&#34;来自黑客,他们小心地覆盖堆栈,所以跳转到了#34;他们的地方&#34;)。