C缓冲区和混乱,初学者

时间:2018-07-14 19:04:35

标签: c string printf c-strings

我今天一直在弄乱c语言,当我注释掉这段代码中的第三个缓冲区时,我不理解输出的差异:

 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 void main() {
     unsigned char letters[10];
    memset(letters, 0x00, 10);
    memset(letters, 0x41, 10);
    printf(letters);
    printf(" Total buffer len: %d bytes\n",strlen(letters));

     char nletters[10];
    memset(nletters, 0x00, 10);
    memset(nletters, 0x42, 10);
     printf(nletters);
    printf(" Total buffer len: %d bytes\n",strlen(nletters));

     int nums[10];
     memset(nums, 0x00, 10);
    memset(nums, 0x43, 10);
    printf(nums);
    printf(" Total buffer len: %d bytes\n",strlen(nums));   
 return 0;
}

区别在于删除了nums缓冲区周围的注释:

AAAAAAAAAA�7ǝ�U Total buffer len: 16 bytes
BBBBBBBBBBAAAAAAAAAA�7ǝ�U Total buffer len: 26 bytes

并保留缓冲区:

AAAAAAAAAA Total buffer len: 10 bytes
BBBBBBBBBBAAAAAAAAAA Total buffer len: 20 bytes
CCCCCCCCCC��U Total buffer len: 14 bytes

我没有得到的是:

  1. 出于对圣洁众人的热爱,注释掉第三个缓冲区如何影响其他缓冲区的大小?

  2. 缓冲区末尾的额外字节是什么?如何丢失/管理它们(如果我选择串联缓冲区)?

  3. 当我选择是否对第三个缓冲区进行注释时,为什么打印缓冲区大小和初始化大小的差异不一致?

  4. 缓冲区2应该是10个字节,为什么是20个字节?我不希望它是20,我只要求10。我不认为这是不合理的。

3 个答案:

答案 0 :(得分:2)

  1. 构造堆栈帧时,一些辅助数据被压入堆栈,并且可能包含零,也可能不包含零,这最终标志着溢出缓冲区的结束
  2. C字符串以零标记结尾。前两个缓冲区的结尾不是以零结尾,而是CPU专用并读取内存,直到它实际找到零为止。通常某个地方会有一个...
  3. 未初始化的缓冲区包含以前使用时留在内存中的所有数据
  4. 堆栈在内存中“向下”增长,因此第一个缓冲区在地址“ 50”处结束,第二个缓冲区在地址“ 40”处结束,第三个缓冲区在地址“ 0”处结束。但是,当您从地址“ 40”开始打印第二个缓冲区时,它将向上读取内存,因此10 B-s,10 A-s和一些字节直到找到零。

读数:null-terminated stringbuffer overflowstack things

C字符串的字符串末尾需要一个额外的\ 0字符,因此您的“字母”和“ nletters”可以存储9字母的实际字符串以及零终止符(默认情况下,您必须将其放置在自己的位置)。 'nums'是一个整数数组,它并不真正适合存储字符串,但是C / C ++不会阻止您这样做。这就是为什么我在上面写“ 40”作为第二个缓冲区的虚拟地址:“ nums”很可能是一个具有32位整数的4x10字节缓冲区。

答案 1 :(得分:0)

以下建议的代码纠正了OP发布代码中的许多(大多数)问题。

注意main()函数签名的正确声明

请注意代码缩进的一致使用

请注意使用适当的水平间距以提高可读性

请注意,每次调用printf()

时都要使用正确的格式参数

请注意使用sizeof返回缓冲区的大小(根据printf语句声称它们正在显示的内容

请注意,sizeofstrlen()都返回size_t,而不是int

请注意消除magic数字(例如10)

请注意,应清除不使用那些内容的头文件

现在,建议的代码:

#include <string.h>
#include <stdio.h>

#define MAX_LEN 10



int main( void )
{
    unsigned char letters[ MAX_LEN ];
    memset( letters, 0x00, sizeof( letters ) );
    memset( letters, 0x41, sizeof( letters )-1 );  // keep NUL byte at end
    printf( "%s\n", letters );   //format the output,
                                 // use \n so immediately output to terminal
    printf( " Total buffer len: %lu bytes\n", sizeof(letters) );

    char nletters[ MAX_LEN ];
    memset( nletters, 0x00, sizeof( nletters ) );
    memset( nletters, 0x42, sizeof( nletters )-1 );  // keep NUL byte at end
    printf( "%s\n", nletters );   // format the output,
                                  // use \n so immediately output to terminal
    printf( " Total buffer len: %lu bytes\n", sizeof(nletters) );

    int nums[ MAX_LEN ];            // 10 integers
    memset( nums, 0x00, 10* sizeof( int ) );
    memset( nums, 0x43, 9 );  // this only sets first 10 bytes
                              // NOTE:  sizeof( int ) not same as size of char
                              //   so most of array not modified
    for( size_t i=0; i< MAX_LEN; i++ )
    {
        printf( "%d\n", nums[ i ] );
    }
    printf( " Total buffer len: %lu bytes\n", sizeof(nums) );
    return 0;
}

运行以上代码将得到以下输出:

AAAAAAAAA
Total buffer len: 10 bytes
BBBBBBBBB
Total buffer len: 10 bytes
1128481603
1128481603
67
0
0
0
0
0
0
0
Total buffer len: 40 bytes

答案 2 :(得分:0)

您说您一直在搞C,但是不是C。这破坏了C的某些规则。如果您破坏了C的规则,就会发生奇怪的事情 ...我的主要问题您是:您正在阅读哪本书?,因为您当前的那本书对您来说效果不佳...


在C中,字符串以'\0'结尾。由于letters不是包含'\0'字符的字符序列,因此它不是 string ,因此,您不应将其视为一个字符。如果您要在一个字符串中包含10个字符,则实际上至少需要一个 11 数组来为'\0'腾出空间(您还需要在{{1之后}}。

memset

在C中,char letters[11]; memset(letters, 'a', 10); letters[10] = '\0'; 格式说明符用于打印%zu的值,例如从size_t返回的值。 strlen用于打印%d值,仅

int

  

出于对所有圣洁者的热爱,注释掉第三个缓冲区如何影响其他缓冲区的大小?

printf("%s\n", letters); printf("strlen(letters): %zu\n", strlen(letters)); printf期望它们的参数为 strings ,但是 string 必须始终包含strlen 。您的数组不包含'\0',因此与字符串相关的函数会循环出界并处理超出范围的数据。

作为练习,预测'\0' ...检验您的理论。


  

缓冲区末尾的多余字节是什么?如何丢失/管理它们(如果我选择串联缓冲区)?

那些多余的字节是未定义的行为,这是一组令人恐惧的单词,意思是“任何事情都可以代替,因为您违反了规则”” ...当您违反C语言的规则时,会发生奇怪的事情 ...


  

当我选择是否注释第三个缓冲区时,为什么打印缓冲区大小和初始化大小的差异不一致?

再次,不确定的行为,违反规则... 发生奇怪的事情 ...和您正在阅读哪本书?我问的原因似乎是有人使用书本可以很快解决这个问题,所以我认为您只是在猜测 (在C语言中很危险)。通过阅读一本书,您将更快地学习C(就像在一样)。


  

缓冲区2应该是10个字节,为什么是20个字节?我不希望它是20,我只要求10。我不认为这是不合理的。

在没有C的情况下,停止告诉C有一个字符串(由一个字符序列组成的strlen((char[]) { 1, 2, 3, 4, '\0', 5 }))。