- 主题:请教一个字符串的问题
给标准和理解点赞
【 在 lele 的大作中提到: 】
: 标 题: Re: 请教一个字符串的问题
: 发信站: 水木社区 (Mon Jul 25 06:04:12 2022), 站内
:
: 你的“标准回答”实际上并不符合 C语言标准 :-)
:
: 先说说第二点。
: 以下是 C语言标准对格式化输出函数(例如 printf)中 "s" 标识符的解释
:
: C89 4.9.6.1:
: The argument shall be a pointer to an array of character type. Characters from the array are written up to (but not including) a terminating null character; if the precision is specified, no more than that many characters are written. If the precis
: ion is not specified or is greater than the size of the array, the array shall contain a null character.
:
: C99 7.19.6.1:
: If no l length modifier is present, the argument shall be a pointer to the initial element of an array of character type. Characters from the array are written up to (but not including) the terminating null character. If the precision is specified,
: no more than that many bytes are written. If the precision is not specified or is greater than the size of the array, the array shall contain a null character.
:
: 两个版本均表明 "%s" 标识符指代的是字符数组。
:
: 实际上, C语言对“字符串”的定义是:
:
: C89 4.1.1 / C99 7.1.1:
: A string is a contiguous sequence of characters terminated by and including the first null character.
:
: 也就是说,“字符串”实际上是字符数组的一个子集。
:
:
: 回到楼主的问题,严格意义上来说和“字符串”并没有关系。
: 这是因为他的程序第6行
:
: char a[5] = "12345";
:
: 涉及的实际上是“字符串常量”,即 character string literal。
: “字符串常量”的定义如下:
:
: C89 3.1.4 / C99 6.4.5
: A character string literal is a sequence of zero or more multibyte characters enclosed in double-quotes, as in "xyz".
:
: C89 中给出的语义是:
: A character string literal has static storage duration and type "array of char", and is initialized with the given characters. ... A null character is then appended.
:
: C99 中对编译器的行为进行了进一步的规范:
: In translation phase 6, the multibyte character sequences specified by any sequence of adjacent character and wide string literal tokens are concatenated into a single multibyte character sequence.
: In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals.
:
:
: 也就是说,字符串常量 "12345" 最终要被编译器解释为包含6个元素的字符数组,其中最后一个元素是编译器添上的结束符 '\0'。
: 顺便一提,C语言标准在脚注中明确指出了“字符串常量”和“字符串”的区别:
:
: A character string literal need not be a string, because a null character may be embedded in it by a \0 escape sequence.
:
:
: 那么当将这个字符串常量赋予长度仅为 5 的字符数组时,会发生什么呢?
: C语言标准是这么规定的:
:
: C89 3.5.7 / C99 6.7.8
: An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unkno
: wn size) initialize the members of the array.
:
: 简单来说,就是如果字符数组长度已知的话,就从字符串常量往字符数组挨个放字符,直到放满为止。
: 那么楼主这行代码实际上是符合 C语言规范的。这就是为什么编译器不会报错。
: 最终的结果是,字符串常量 "12345" 末尾那个由编译器加上的 '\0' 并不会赋予数组 a 。
: 实际上,这行代码翻译成汇编是
:
: a:
: .ascii "12345"
:
: 而
:
: char a[] = "12345";
:
: 对应的汇编则是
:
: a:
: .asciz "12345"
:
:
:
: 楼主代码中的问题出在
:
: printf("%s",a);
:
: 这一行。因为 C语言标准规定,当格式化输出所用的标识符为 "s" 时,
:
: C89 4.9.6.1 / C99 7.19.6.1
: If the precision is not specified or is greater than the size of the array, the array shall contain a null character.
:
: 意即,数组 a 必须包含字符 '\0'。
:
: 所以,楼主这种 a 不含 '\0' 的情况,确实属于“未定义行为”(undefined behavior)。
: 为什么说这是“未定义行为”呢?因为 C语言标准定义了“未定义行为”:
:
: C89 A.6.2
:
: The format for the fprintf or fscanf function does not match the argument list
:
: C99 J.2
:
: An s conversion specifier is encountered by one of the formatted output functions, and the argument is missing the null terminator (unless a precision is specified that does not require null termination).
:
: 应该说,C99 的规定更加明确。
:
: 最后,个人观点,熟悉了 C 的基本语法后,C语言标准是学习 C 的最好教材。
:
:
: 【 在 Naory 的大作中提到: 】
: : 标 题: Re: 请教一个字符串的问题
: : 发信站: 水木社区 (Sun Jul 24 09:40:30 2022), 站内
: :
: : 来个标准回答:
: :
: : 第一:
: :
: : char a[5] = "12345";
: :
: : 这里只是定义了一个数组 a(数组中的每个元素是一个 char),并不是定义了一个字符串
: : 所以从 a[0] 到 a[4] 分别是 char:'1' 到 '5'
: :
: : 而这个数组的长度是 5,只能使用 a[0] 到 a[4],
: : 并没有给 a[5] 分配空间,不能去使用 a[5]
: :
: : 第二:
: :
: : printf("%s", a) ,如果 a 不是「字符串」,就是「未定义行为」;
: :
: : 而这里的 a 是一个「数组」,不是「字符串」,就是一个「未定义行为」。
: :
: : 所谓「未定义行为」,就是任何事情都可能发生,可能符合你的期望,也可能不符合;
: :
: : 作为 C 程序员,目标就是不要写出「未定义行为」的代码
: :
: : 【 在 shouge111 的大作中提到: 】
: : : 变量a长度是5,那么根据教材,它只能存储1234,最后一个存储单元应该存储字符串结束标志\0。
: : : 但是实际运行,a存储了12345.这是怎么回事呢?printf打印也是打印了12345
: : : #include<stdio.h>
: : : ...................
: :
: : --
: :
: : ※ 来源:·水木社区 mysmth.net·[FROM: 101.71.39.*]
:
: --
:
: ※ 来源:·水木社区 mysmth.net·[FROM: 92.106.208.*]
--
FROM 182.204.52.*
我没像你这样来解释,主要是担心楼主不太好理解。
实际上,当然是严格按标准来好,不过也要考虑别人当前的接受程度
【 在 lele 的大作中提到: 】
: 你的“标准回答”实际上并不符合 C语言标准 :-)
: 先说说第二点。
: 以下是 C语言标准对格式化输出函数(例如 printf)中 "s" 标识符的解释
: ...................
--
FROM 101.71.39.*
re
我在二楼、四楼和六楼分别给了一个适合楼主目前接受程度的解释。
我看楼主也挺满意的。
后面热心网友们又发挥了很多。
【 在 Naory 的大作中提到: 】
: 我没像你这样来解释,主要是担心楼主不太好理解。
: 实际上,当然是严格按标准来好,不过也要考虑别人当前的接受程度
--
修改:flw FROM 163.125.197.*
FROM 163.125.197.*
这种会自动加\0,sprintf这种函数,也会在后面加个\0
【 在 e729 的大作中提到: 】
: 哈哈,同问
: #include <stdio.h>
: int main()
: ...................
--
FROM 113.108.41.*
个人觉得是巧合吧,先分配5字节空间给数组然后从RO区域拷贝12345到数组缓存区 ,12345后面的值应该是一个随机值如果是刚上电 RAM区域很多地方都被清零过 \0=0h 刚好打印正常。
【 在 BirdFans 的大作中提到: 】
: 这种会自动加\0,sprintf这种函数,也会在后面加个\0
--
FROM 112.0.52.*
是的,因为内存使用率一般比较低,后面大概率是\0,刚好结果是对的
【 在 qzyanyuan 的大作中提到: 】
: 个人觉得是巧合吧,先分配5字节空间给数组然后从RO区域拷贝12345到数组缓存区 ,12345后面的值应该是一个随机值如果是刚上电 RAM区域很多地方都被清零过 \0=0h 刚好打印正常。
--
FROM 113.108.41.*
看编译器
一般编译就会报错,这不数组越界吗.....
【 在 shouge111 的大作中提到: 】
: 变量a长度是5,那么根据教材,它只能存储1234,最后一个存储单元应该存储字符串结束标志\0。
: 但是实际运行,a存储了12345.这是怎么回事呢?printf打印也是打印了12345
: #include<stdio.h>
: ...................
--
FROM 58.250.240.*
嗯,是的。就是不要这么做,写正确程序
【 在 jsj020107 的大作中提到: 】
: 看编译器
: 一般编译就会报错,这不数组越界吗.....
--
FROM 112.32.8.*