通过考虑将内存分为四个部分:数据,堆,堆栈和代码,全局变量,静态变量,常量数据类型,局部变量(在函数中定义和声明),变量(在main函数中),指针,并动态分配空间(使用malloc和calloc)存储在内存中?
我认为他们将分配如下:
char *arr,int *arr)------->堆我只是从C的角度来指这些变量.
如果我错了,请纠正我,因为我是C的新手.
几乎每个读这篇文章的人都熟悉这三个关于C的关键事实:
[]与指针一样适用于指针.这三个事实绝对是C中数组和指针处理的核心.它们甚至不是三个独立的事实; 它们是一个中心概念的相互关联的方面.如果没有对这个概念的正确理解,就不可能正确地进行相当基本的C编程.
我今天的问题很简单,这个概念的名称是什么?
我以为我是老式的,但我总是把它称为"C中数组和指针之间的等价",或简称为"数组/指针等价".但我知道你几乎不能在SO上说出这些话; 他们几乎是禁忌.
这可能看起来像一个抽象或哲学问题,所以更具体地构建它,我正在寻找的是一个简单的名词或名词短语我可以在句子中使用"是的,由于_____,数组下标可以被认为是作为指针算术的语法糖",回答,比方说,这个问题.
(但是请注意,我不是在寻找这个问题的答案,也不是为了回答"'等价'这个词有什么问题?".是的,我知道,它会误导学习者想象数组和指针是某种方式同样.当我写这个FAQ列表条目时,我确实有这种困惑.)
我正在制作自己的 C 编译器,以尝试尽可能多地了解有关 C 的详细信息。我现在正在尝试准确地了解volatile对象是如何工作的。
令人困惑的是,代码中的每个读访问都必须严格执行(C11,6.7.3p7):
具有 volatile 限定类型的对象可能会以实现未知的方式进行修改,或者产生其他未知的副作用。因此,引用此类对象的任何表达式都应严格根据抽象机的规则进行评估,如 5.1.2.3 中所述。此外,在每个序列点,最后存储在对象中的值应与抽象机规定的值一致,除非受到前面提到的未知因素的修改。134)构成对具有易失性限定类型的对象的访问的是实现-定义。
示例:在 中a = volatile_var - volatile_var;,必须读取 volatile 变量两次,因此编译器无法优化a = 0;
同时,序列点之间的评估顺序未确定(C11,6.5p3):
运算符和操作数的分组由语法指示。除非稍后指定,否则子表达式的副作用和值计算是无序的。
示例:b = (c + d) - (e + f)未指定计算添加的顺序,因为它们是无序的。
但是,对未排序对象的评估会产生副作用(例如volatile),行为未定义(C11,6.5p2):
如果标量对象上的副作用相对于同一标量对象上的不同副作用或使用同一标量对象的值的值计算是无序的,则行为是未定义的。如果表达式的子表达式有多个允许的排序,并且在任何排序中出现此类未排序的副作用,则行为未定义。
x = volatile_var - (volatile_var + volatile_var)这是否意味着像未定义这样的表达式?如果发生这种情况,我的编译器应该发出警告吗?
我尝试看看 CLANG 和 GCC 做了什么。既不抛出错误也不发出警告。输出的 asm 显示变量不是按执行顺序读取的,而是从左到右读取的,如下面的 asm risc-v asm 所示:
const int volatile thingy = 0;
int main()
{
int new_thing = thingy - (thingy + thingy);
return …Run Code Online (Sandbox Code Playgroud) 一个标准的习语是
while(std::getline(ifstream, str))
...
Run Code Online (Sandbox Code Playgroud)
所以如果这有效的话,我为什么不能说
bool getval(std::string &val)
{
...
std::ifstream infile(filename);
...
return std::getline(infile, val);
}
Run Code Online (Sandbox Code Playgroud)
g++ 说“ cannot convert 'std::basic_istream<char>' to 'bool' in return”。
值函数return中语句的布尔bool上下文是否与 的布尔上下文有所不同while(),以至于在一个上下文中执行的魔术转换std::basic_istream在另一个上下文中不起作用?
附录:这里显然有一些版本,也许还有语言标准依赖性。我在 g++ 8.3.0 中遇到了上述错误。但我在 gcc 4.6.3 或 LLVM (clang) 9.0.0 中没有得到它。
在 C 中,
{0}通用零初始值设定项与 C++ 的通用零初始值设定项等效{}(后者在 C 中无效)。每当您想要一个完整但概念上不透明或实现定义类型的零初始化对象时,就有必要使用它。C 标准库中的经典示例是mbstate_t:Run Code Online (Sandbox Code Playgroud)mbstate_t state = { 0 }; /* correctly zero-initialized */与常见但不可移植的相比:
Run Code Online (Sandbox Code Playgroud)mbstate_t state; memset(&state, 0, sizeof state);
让我感到奇怪的是,后一个版本可能是不可移植的(即使对于实现定义的类型,编译器也必须知道大小)。这里的问题是什么?什么时候是memset(x, 0, sizeof x)不可移植的?
我以为我理解了继承,虚函数和函数重载,但我有一个案例,其中有关于这些特性之间的相互作用的事情让我望而却步.
假设我有一个包含重载虚函数的简单基类,以及从中派生的第二个类:
class b {
public:
virtual int f() { return 1; }
virtual int f(int) { return 2; }
};
class d : public b {
public:
virtual int f(int) { return 3; }
};
Run Code Online (Sandbox Code Playgroud)
请注意,派生类d仅覆盖其中一个重载的虚函数.
我可以实例化一个类的对象d并f(int)在其上调用,没问题:
d x;
std::cout << x.f(0) << std::endl;
Run Code Online (Sandbox Code Playgroud)
但是当我尝试调用0参数函数时:
std::cout << x.f() << std::endl;
Run Code Online (Sandbox Code Playgroud)
它失败!gcc说"没有匹配函数来调用'd :: f()';候选者是:virtual int d :: f(int)".clang说"函数调用的参数太少,预期为1,有0;你的意思是'b :: f'吗?" 即使d派生出来的b是0-argument f()方法,编译器也会忽略它,并试图调用d1-argument方法.
我可以通过重复派生类中的0参数函数的定义来解决这个问题:
class d : public …Run Code Online (Sandbox Code Playgroud) 我有一份未经我证实但来自可靠来源的报告,该代码
qsort(a, n, sizeof *a, cmpfunc);
Run Code Online (Sandbox Code Playgroud)
由现代版本的 gcc 编译,就好像它已被编写一样
if(n == 0)
__builtin_trap();
qsort(a, n, sizeof *a, cmpfunc);
Run Code Online (Sandbox Code Playgroud)
qsort显然,调用with被认为n == 0是未定义的行为。
[编辑:这里的整个前提被发现是错误的;请参阅下面的“更新 2”。]
有人指出,Posix 明确支持这种n == 0情况,但显然现有的 C 标准版本不支持这种情况。
所以显而易见的问题是:
qsort吗?n = 0qsort任意调用的程序n是否真的有义务检查n == 0而不是qsort在这种情况下调用?qsortwithn == 0是未定义的,这似乎也会稍微减慢每个非未定义程序的速度。快速排序的教科书实现(我知道,这qsort不是必需的)几乎无法n = 0正确处理。我想知道 gcc 在这里的行为是否试图防止一种实现,该实现以某种方式做比if 初始调用qsort更糟糕的事情? …
这通常被建议,作为将结构体初始化为零值的方法:
struct foo f = {0};
Run Code Online (Sandbox Code Playgroud)
还提到{}可以在gcc下使用,但这不是标准的C99.
我想知道这是否适用于一个结构,其布局可能会超出我的控制范围.我很担心,因为0它不是数组或结构的有效初始值设定项.然而,gcc --std=c99(gcc-8.1.1-1.fc28.x86_64)似乎也接受{0}了这种情况.
问题 C99是否接受{0}任何结构的初始化程序?
(或者是后来的C标准?还是相反的,是否有任何理由不依赖于此?是否存在{0}可能导致错误或警告会阻止其使用的编译器?)
我试过了什么
gcc警告(启用-Wall)表明这是标准中的某种形式的边缘情况,其中gcc被强制接受0作为任何类型的struct字段的初始化器,但它会警告它,除非你使用常见的{0}习惯用法.
struct a { int i; };
struct b { struct a a; struct a a2; };
struct c { int i[1]; int j[1]; };
struct a a = {0}; /* no error */
struct b b = {0}; /* no error */
struct c c = {0}; /* …Run Code Online (Sandbox Code Playgroud) ntp 不想跳过你的时钟,因为不连续的时间跳跃是不好的。它想逐渐调整你的时钟——非常缓慢。这是非常保守的:默认情况下,它不会使您的时钟偏移超过百万分之 xx (ppm)。
但是由于 ntp 非常保守,如果它发现您的时钟太远,以至于逐渐调整它会花费很长时间,它无论如何都会回退并跳过您的时钟(即使那很糟糕)。默认情况下,如果逐渐调整时钟所需的时间超过 yy 小时,则会执行此操作。
如果您愿意,您可以告诉 ntp 更快地调整您的时钟(即逐渐减少),最高可达 zz ppm。
我的问题是,xx、yy 和 zz 是什么?我知道这些阈值存在,我很确定它们已记录在案,我很确定它们是可配置的,但我永远记不起这些值,也找不到它们。
如果您知道它们是什么,理想情况下我想知道 (a) 默认值是什么和 (b) 它们的文档位置和 (c) 如何配置它们以及 (d) 实际代码在哪里做出回转或跳跃的决定。谢谢。
假设我想从 读取stdin,并让用户输入包含空字符的字符串。fgets这可以通过or等字符串输入函数实现吗gets_s?或者我必须使用例如fgetc或fread?
这里有人想这样做。
c ×7
c++ ×2
arrays ×1
boolean ×1
c11 ×1
ifstream ×1
inheritance ×1
input ×1
memory ×1
memset ×1
ntp ×1
overloading ×1
pointers ×1
portability ×1
qsort ×1
struct ×1
terminology ×1
types ×1
volatile ×1