Tag: 语言律师

在指针到T,T数组和指向数组的T之间转换是否有未定义的行为?

请考虑以下代码。 #include int main() { typedef int T; T a[] = { 1, 2, 3, 4, 5, 6 }; T(*pa1)[6] = (T(*)[6])a; T(*pa2)[3][2] = (T(*)[3][2])a; T(*pa3)[1][2][3] = (T(*)[1][2][3])a; T *p = a; T *p1 = *pa1; //T *p2 = *pa2; //error in c++ //T *p3 = *pa3; //error in c++ T *p2 = **pa2; T […]

初始化与C中的赋值

我的导师最近告诉C中的数组初始化有两种方式,即: 手动像int a[5]={1,2,3,4,5}; 使用scanf()如int a[5], i; for(i=0;i<5;i++) scanf("%d", &a[i]); int a[5], i; for(i=0;i<5;i++) scanf("%d", &a[i]); 在我看来,第二种“方式”是一种分配而不是初始化的方式 。 所以我决定检查这里的人们对此有何看法。 我偶然发现了一篇文章,其中一个答案声称: 如果您要问的是术语(*从您的问题中并不是很清楚),那么变量的“初始化”实际上是第一次为其赋值。 这个术语来自于你给变量它的“初始”值。 这应该(显然)在第一次使用之前发生。 int x=5; 是一个声明和一个初始化,实际上只是方便的简写 int x; x=5; 如果我要遵循这个特定答案所声称的内容,那么第二种“初始化”方式是正确的,因为在scanf()语句之前没有赋值。 但是,由于我对静态变量的了解,我脑子里出现了一个新的疑问。 请考虑以下代码: #include void first_way(){ static int x[2]={1,2}; printf(“first_way called %d time(s)\n”,++x[0]); } void second_way(){ int i; static int x[2]; for(i=0;i<2;i++)scanf("%d",&x[i]); printf("second_way called %d time(s)\n",++x[0]); } […]

指向不同对象的指针的相等比较

受此回答这个问题的启发,我对C11和C99标准进行了一些挖掘,以便在指针上使用相等运算符(原始问题涉及关系运算符)。 这是C11在§6.5.9.6中所说的(C99类似): 两个指针比较相等,当且仅当两个都是空指针时,两者都是指向同一对象的指针(包括指向对象的指针和在其开头的子对象)或函数,两者都是指向同一数组的最后一个元素之后的指针对象,或者一个是指向一个数组对象末尾的指针,另一个是指向不同数组对象的开头的指针,该数组对象恰好跟随地址空间中的第一个数组对象。 94 ) 脚注94说(并注意脚注是非规范性的): 两个对象在内存中可能是相邻的,因为它们是较大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择放置它们,即使它们是不相关的。 如果先前的无效指针操作(例如数组边界外的访问)产生了未定义的行为,则后续比较也会产生未定义的行为。 案文正文和非规范性说明似乎存在冲突。 如果一个人认真对待文本正文中的“if if only only” ,那么在任何其他情况下都不应该返回平等,并且UB没有空间。 所以,例如这段代码: uintptr_t a = 1; uintptr_t b = 1; void *ap = (void *)a; void *bp = (void *)b; printf (“%d\n”, ap <= bp); /* UB by §6.5.8.5 */ printf ("%d\n", ap < bp); /* UB by §6.5.8.5 */ printf ("%d\n", […]

使用fgetc时,是否可以将EOF与正常的字节值混淆?

我们经常使用这样的fgetc : int c; while ((c = fgetc(file)) != EOF) { // do stuff } 从理论上讲,如果文件中的某个字节的值为EOF ,则此代码存在错误 – 它会提前中断循环并且无法处理整个文件。 这种情况可能吗? 据我所知, fgetc内部将从文件读取的字节转换为unsigned char ,然后转换为int ,并返回它。 如果int的范围大于unsigned char的范围,这将起作用。 如果不是(可能那么sizeof(int)=1 )会发生什么? fgetc有时会从文件中读取等于EOF的合法数据吗? 它会改变从文件中读取的数据以避免单值EOF吗? fgetc会是一个未实现的function吗? EOF会不会是另一种类型,比如long ? 我可以通过额外的检查使我的代码变得简单: int c; for (;;) { c = fgetc(file); if (feof(file)) break; // do stuff } 如果我想要最大的便携性是必要的吗?

void *总是与char *具有相同的表示吗?

void*总是与char*具有相同的表示吗? 细节: 我想使用一个可变参数函数,它使char *被一个(char*)0终止,如下所示: int variadic(char*, …); //<-prototype variadic("foo", "bar", (char*)0); //<- usage 我想用NULL替换(char*)0 ,但是从http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf来判断: 66)宏NULL在(和其他头部)中定义为空指针常量; 见7.19。 3值为0的整型常量表达式或类型为void *的表达式称为空指针常量。 66)如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较。 我不能,因为在variadic上下文中,我绝对需要一个char*和一个普通的0是不可接受的。 如果我定义: #define NIL (void*)0 /*<= never a plain 0*/ 用它来终止我的variadic(char*,…)对我来说是否合法?

是否有一种简单,可移植的方法来确定C中两个字符的排序?

根据标准: 执行字符集的成员值是实现定义的。 (ISO / IEC 9899:1999 5.2.1 / 1) 进一步在标准中: …在上面的十进制数字列表中, 0之后的每个字符的值应该比前一个值大1。 (ISO / IEC 9899:1999 5.2.1 / 3) 看来该标准要求执行字符集包括拉丁字母表的26个大写字母和26个小写字母,但我认为不要求以任何方式对这些字符进行排序。 我只看到十进制数字的订单规定。 这似乎意味着,严格地说,不能保证’a’ < 'b' 。 现在,字母表中的字母按顺序分为ASCII,UTF-8和EBCDIC。 但是对于ASCII和UTF-8,我们有’A’ < 'a' ,而对于EBCDIC,我们有’a’ < 'A' 。 在ctype.h中有一个可以移植地比较字母字符的函数可能会很好。 除了这个或类似的东西,在我看来,必须在语言环境中查找CODESET的值并相应地继续,但这似乎并不简单。 我的直觉告诉我,这几乎不是问题; 对于大多数情况,可以通过转换为小写来处理字母字符,因为对于最常用的字符集,字母是有序的。 问题:给出两个字符 char c1; char c2; 是否有一种简单,可移植的方法来确定c1是否按字母顺序排在c2之前? 或者我们是否假设小写和大写字符总是按顺序出现,即使标准似乎不能保证这一点? 为了澄清任何混淆,我真的只对拉丁字母的52个字母感兴趣,这些字母由标准保证在执行字符集中。 我意识到其他字母组很重要,但似乎我们甚至无法知道这一小部分字母的排序。 编辑 我想我需要澄清一点。 我认为,问题在于我们通常认为拉丁字母的26个小写字母是有序的。 我希望能够断言“a”出现在’b’之前,当我们给出’a’和’b’整数值时,我们有一种方便的方式在代码中将其表示为’a’ < 'b' 。 但该标准不保证上述代码将按预期进行评估。 为什么不? […]

uint8_t可以是非字符类型吗?

在这个答案和附带的评论中, Pavel Minaev提出以下论点:在C中, uint8_t可以被typedef的唯一类型是char和unsigned char 。 我正在看这个 C标准草案 。 uint8_t的存在意味着存在相应的类型int8_t (7.18.1p1)。 int8_t是8位宽,没有填充位(7.18.1.1p1)。 相应的类型具有相同的宽度(6.2.5p6),因此uint8_t也是8位宽。 unsigned char是CHAR_BIT位宽(5.2.4.2.1p2和6.2.6.1p3)。 CHAR_BIT至少为8(5.2.4.2.1p1)。 CHAR_BIT最多为8,因为uint8_t是unsigned char ,或者是非unsigned char ,非位字段类型,其宽度是CHAR_BIT (6.2.6.1p4)的倍数。 根据这个论点,我同意,如果uint8_t存在,那么它和unsigned char都有相同的表示:8个值位和0个填充位。 这似乎并不强迫它们属于同一类型(例如,6.2.5p14)。 是否允许uint8_t被类型定义为扩展无符号整数类型(6.2.5p6),其表示forms与unsigned char相同? 当然它必须是typedef’d(7.18.1.1p2),并且它不能是除unsigned char之外的任何标准无符号整数类型(如果碰巧是无符号的则为char )。 这个假设的扩展类型不是字符类型(6.2.5p15),因此不符合对不兼容类型(6.5p7)的对象的别名访问,这让我觉得编译器编写者想要这样做的原因事情。

需要帮助了解\ n,\ b和\ r \ n将如何呈现printf输出

我在C中编写了以下程序,当我运行它时,我惊讶于查看输出。 这是该计划 int main() { printf(“\nab”); printf(“\bsi”); printf(“\rha”); } 输出是: – hai而我期待“absiha”,因为\ n是新行,\ b是退格(非擦除),\ r是回车。 所以我期待那个光标会处于“i”角色,因为\ r \ n已被应用但是当我运行它并看到输出时我感到非常惊讶和困惑。 有人可以解释一下输出吗?

用scanf检测积分溢出

在最近回答另一个问题时 ,我发现代码有问题,例如: int n; scanf (“%d”, &n); 使用strtol ,您可以检测溢出,因为在这种情况下,允许的最大值被插入到n并且errno被设置为指示溢出,根据C11 7.22.1.4 The strtol, strtoll, strtoul, and strtoull functions /8 : 如果正确的值超出可表示值的范围,则返回LONG_MIN,LONG_MAX,LLONG_MIN,LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据值的返回类型和符号,如果有),并且宏ERANGE的值为存储在errno中。 但是,在标准处理scanf ,特别是C11 7.21.6.2 The fscanf function /10 ,我们看到: 如果此对象没有适当的类型,或者无法在对象中表示转换结果,则行为未定义。 现在,对我而言,这意味着可以返回任何值,并且没有提到errno被设置为任何东西。 这一点已经曝光,因为上面链接问题的提问者将9,999,999,999输入到32位int并返回1,410,065,407 ,值2 33太小,表明它只是在类型的极限处缠绕。 当我尝试它时,我得到了2,147,483,647 ,这是最大可能的32位无符号值。 所以我的问题如下。 在使用scanf系列函数时,如何以便携方式检测积分溢出? 它甚至可能吗? 现在我应该提一下,在我的系统(Debian 7)中,在这些情况下, errno实际上设置为ERANGE但我在标准中找不到任何要求。 此外, scanf的返回值为1 ,表示扫描项目成功。

标准C中的约束是什么?

C标准谈论约束 ,例如ISO / IEC 9899:201x定义术语 约束 限制,无论是句法还是语义,通过它来解释语言元素的阐述 并在章节一致性中说 如果违反约束或运行时约束之外的”shall”或”shall not”要求,则行为未定义。 据说环境 ,分段诊断这一章 如果预处理转换单元或转换单元包含违反任何语法规则或约束的情况,则符合的实现应生成至少一个诊断消息(以实现定义的方式标识),即使该行为也明确指定为未定义或实现 – 定义。 因此,重要的是要知道C中的约束是什么,例如,编译器编写者判断何时需要诊断,或者C编程人员可以预期诊断而不仅仅是未定义的行为。 现在,标准文档中有一些标题为Constraints的部分 ,但是我找不到关于标准中术语约束究竟是什么的明确措辞。 约束是否出现在标题为约束的部分中? 在这些部分之外陈述的每项要求都不是约束条件吗? 我错过了标准中的约束的全面描述吗?