通过sizeof和strlen认识字符串常量

本文探讨了C语言中字符串常量的特性,包括使用sizeof和strlen获取字符串长度的区别,以及字符串常量的可写性问题。通过示例代码解释了如何正确处理字符串常量以避免运行时和编译时错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过sizeof和strlen认识字符串常量

shawpinlee posted @ 2007年09月15日 08:35PM in C/C++
C/C++试题 -Part 1

字符串型常量

字符串常量是双引号中的字符序列(可能是空的)。可以用字符常量所用的转义机制表示字符串中的字符。标准C语言允许在字符串型常量前面加上L前缀来指定宽字符串常量。

对每个n字符的非宽字符串常量,运行时静态分配n+1个字符的内存块,其中前n个字符是字符串中的字符,最后一个字符是null字符'/0'。这个内存块是字符串常量的值,类型为char[n+1]。同样,宽字符串常量变成n个宽字符加上一个宽null字符,类型为wchar_t [n+1]。

  1. #include <stdio.h>
  2. #include <string.h>
  3.  
  4. int
  5. main ( int argc, char *argv [ ] )
  6. {
  7.   char str [ ] = "hello";
  8.  
  9.   printf ( "strlen(/"%s/") = %d sizeof(/"%s/") = %d/n", str, strlen (str ), str,
  10.            sizeof (str ) );
  11.   printf ( "strlen(/"%s/") = %d sizeof(/"%s/") = %d/n", "hello",
  12.           strlen ( "hello" ), "hello", sizeof ( "hello" ) );
  13.   printf ( "strlen(/"%s/") = %d sizeof(/"%s/") = %d/n", "", strlen ( "" ), "",
  14.            sizeof ( "" ) );
  15.   return 0;
  16. }

结果:

strlen("hello") = 5 sizeof("hello") = 6
strlen("hello") = 5 sizeof("hello") = 6
strlen("") = 0 sizeof("") = 1
 

 

分析:
sizeof操作符返回操作数的长度,而strlen函数返回字符串中的字符数。因此,sizeof("hello")的返回值是6而不是5,sizeof("")的返回值是1而不是0;而strlen("hello")的返回值是5,strlen("")的返回值是0。

  1.         .file    "a.c"
  2.         .section        .rodata                                                              #只读数据段
  3.         . align 4
  4. .LC1:
  5.         .string  "strlen(/"%s/ ") = %d sizeof(/"%s/ ") = %d/n"      #只读
  6. .LC0:
  7.         .string  "hello"                                                                          #只读
  8. .LC2:
  9.         .string  ""
  10.         .text
  11. .globl main
  12.         . type   main, @function
  13. main:
  14.         leal    4 (% esp ), % ecx
  15.         andl    $ -16, % esp
  16.         pushl    -4 (% ecx )
  17.         pushl   % ebp
  18.         movl    % esp, % ebp
  19.         pushl   % edi
  20.         pushl   % ecx
  21.         subl    $ 48, % esp
  22.         movl    .LC0, % eax
  23.         movl    % eax, -14 (% ebp )
  24.         movzwl  .LC0 +4, % eax
  25.         movw    % ax, -10 (% ebp )
  26.         leal    -14 (% ebp ), % eax
  27.         movl    $ -1, % ecx
  28.         movl    % eax, -28 (% ebp )
  29.         movl    $ 0, % eax
  30.         cld
  31.         movl    -28 (% ebp ), % edi
  32.         repnz
  33.         scasb
  34.         movl    % ecx, % eax
  35.         notl    % eax
  36.         leal    -1 (% eax ), % edx
  37.         movl    $ 6, 16 (% esp )
  38.         leal    -14 (% ebp ), % eax
  39.         movl    % eax, 12 (% esp )
  40.         movl    % edx, 8 (% esp )
  41.         leal    -14 (% ebp ), % eax
  42.         movl    % eax, 4 (% esp )
  43.         movl    $.LC1, (% esp )
  44.         call    printf
  45.         movl    $ 6, 16 (% esp )
  46.         movl    $.LC0, 12 (% esp )
  47.         movl    $ 5, 8 (% esp )
  48.         movl    $.LC0, 4 (% esp )
  49.         movl    $.LC1, (% esp )
  50.         call    printf
  51.         movl    $ 1, 16 (% esp )
  52.         movl    $.LC2, 12 (% esp )
  53.         movl    $ 0, 8 (% esp )
  54.         movl    $.LC2, 4 (% esp )
  55.         movl    $.LC1, (% esp )
  56.         call    printf
  57.         movl    $ 0, % eax
  58.         addl    $ 48, % esp
  59.         popl    % ecx
  60.         popl    % edi
  61.         popl    % ebp
  62.         leal    -4 (% ecx ), % esp
  63.         ret
  64.         . size   main, .-main
  65.         .ident  "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)"
  66.         .section        .note.GNU- stack, "",@progbits

存储字符串型常量
不能修改保存字符串型常量字符的内存,因为这个内存可能是只读的,即物理上是防止修改的。有些函数(如mktemp)要接受就地修改的字符串指针,此时不要向这些函数传递字符串型常量,而要将这个字符串型常量的内存初始化到一个非const字符数组中,然后传递数组第一个元素的地址。

  1. #include <stdio.h>
  2.  
  3. int main ( int argc, char* argv [ ] )
  4. {
  5.         char p1 [ ] = "Always writable";
  6.         char *p2 = "Possibly not writable";
  7.         const char p3 [ ] = "Never writable"; /* Standard C only */
  8.         p1 [ 0 ] = 'a';
  9.         p2 [ 0 ] = 'p'; /* runtime error: segment error*/
  10.         p3 [ 0 ] = 'n'; /* compile error: error induced by writting data into read-only postion */
  11.         return 0;
  12. }

 

p1、p2与p3的值都是字符数组的指针,但其可写性不同。赋值语句p1[0] =' a'总是可行的,p2[0] = 'p'会造成运行时错误,而p3[0]='n'总是会造成编译错误,这里由const的含义决定的。

  1. #include <stdio.h>
  2.  
  3. int main ( int argc, char* argv [ ] )
  4. {
  5.         char p1 [ ] = "Always writable";
  6.         char *p2 = "Possibly not writable";
  7.         const char p3 [ ] = "Never writable"; /* Standard C only */
  8.         //p1[0] = 'a';
  9.         //p2[0] = 'p'; /* runtime error: segment error*/
  10.         //p3[0] = 'n'; /* compile error: error induced by writting data into read-only postion */
  11.         return 0;
  12. }
  13.  
  1.         .file   "b.c"
  2.         .section        .rodata
  3. .LC1:
  4.         .string "Possibly not writable"
  5. .LC0:
  6.         .string "Always writable"
  7. .LC2:
  8.         .string "Never writable"
  9.         .text
  10. .globl main
  11.         .type   main, @function
  12. main:
  13.         leal    4(%esp), %ecx
  14.         andl    $-16, %esp
  15.         pushl   -4(%ecx)
  16.         pushl   %ebp
  17.         movl    %esp, %ebp
  18.         pushl   %ecx
  19.         subl    $52, %esp
  20.         movl    4(%ecx), %eax
  21.         movl    %eax, -56(%ebp)
  22.         movl    %gs:20, %eax
  23.         movl    %eax, -8(%ebp)
  24.         xorl    %eax, %eax
  25.         movl    .LC0, %eax
  26.         movl    %eax, -39(%ebp)
  27.         movl    .LC0+4, %eax
  28.         movl    %eax, -35(%ebp)
  29.         movl    .LC0+8, %eax
  30.         movl    %eax, -31(%ebp)
  31.         movl    .LC0+12, %eax
  32.         movl    %eax, -27(%ebp)
  33.         movl    $.LC1, -44(%ebp)
  34.         movl    .LC2, %eax
  35.         movl    %eax, -23(%ebp)
  36.         movl    .LC2+4, %eax
  37.         movl    %eax, -19(%ebp)
  38.         movl    .LC2+8, %eax
  39.         movl    %eax, -15(%ebp)
  40.         movzwl  .LC2+12, %eax
  41.         movw    %ax, -11(%ebp)
  42.         movzbl  .LC2+14, %eax
  43.         movb    %al, -9(%ebp)
  44.         movl    $0, %eax
  45.         movl    -8(%ebp), %edx
  46.         xorl    %gs:20, %edx
  47.         je      .L3
  48.         call    __stack_chk_fail    
  49. .L3:
  50.         addl    $52, %esp
  51.         popl    %ecx
  52.         popl    %ebp
  53.         leal    -4(%ecx), %esp
  54.         ret
  55.         .size   main, .-main
  56.         .ident  "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)"
  57.         .section        .note.GNU-stack,"",@progbits

 

 __stack_chk_fail干什么用的???困惑中......

===========================================================================================

到linuxquestion.org上询问了一下,老外真是热心肠啊,很快就得到了答案。在bbs@ustc,和linuxforum.net上问了都没有人回答啊,这俩个地方不行啊,很少能遇到技术好的并且热心肠的人。以后到国外的论坛上混了。

from : http://www.linuxquestions.org/questions/showthread.php?t=584863

help: puzzle about "__stack_chk_fail"

hello there,

In the process of dissecting an example program from C: A Reference Manual (Fifth Edition) as follows:

=======================================
#include <stdio.h>

int main(int argc, char* argv[])
{
char p1[] = "Always writable";
char *p2 = "Possibly not writable";
const char p3[] = "Never writable"; /* Standard C only */
return 0;
}
=======================================

A puzzle shocked me after I got the assembly of above program:

=======================================
.file "b.c"
.section .rodata
.LC1:
.string "Possibly not writable"
.LC0:
.string "Always writable"
.LC2:
.string "Never writable"
.text
.globl main
.type main, @function
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $52, %esp
movl 4(%ecx), %eax
movl %eax, -56(%ebp)
movl %gs:20, %eax
movl %eax, -8(%ebp)
xorl %eax, %eax
movl .LC0, %eax
movl %eax, -39(%ebp)
movl .LC0+4, %eax
movl %eax, -35(%ebp)
movl .LC0+8, %eax
movl %eax, -31(%ebp)
movl .LC0+12, %eax
movl %eax, -27(%ebp)
movl $.LC1, -44(%ebp)
movl .LC2, %eax
movl %eax, -23(%ebp)
movl .LC2+4, %eax
movl %eax, -19(%ebp)
movl .LC2+8, %eax
movl %eax, -15(%ebp)
movzwl .LC2+12, %eax
movw %ax, -11(%ebp)
movzbl .LC2+14, %eax
movb %al, -9(%ebp)
movl $0, %eax
movl -8(%ebp), %edx
xorl %gs:20, %edx
je .L3
call __stack_chk_fail
.L3:
addl $52, %esp
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
.size main, .-main
.ident "GCC: (GNU) 4.1.2 (Ubuntu 4.1.2-0ubuntu4)"
.section .note.GNU-stack,"",@progbits
=============================================

what is "__stack_chk_fail" for in the assembly?

 

replied by paulsm4

Hi -

This code is simply doing a "sanity check" of the stack before it pops the stack and does a "return":

Code:
...
movl -8(%ebp), %edx
xorl %gs:20, %edx
je .L3
call __stack_chk_fail
.L3:
...

If everything's OK, then the CPU's zero flag will be set, we'll jump to .L3, and we will not call "__stack_chk_fail".

Here's a link describing the GS register (%gs) and "protected mode" memory addressing issues in a bit more detail:
http://my.execpc.com/~geezer/johnfine/segments.htm 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值