鉴于C++ 11标准现在得到了大多数体面编译器的良好支持,2014年使用原始指针的主要原因有哪些?
我确定了几个场景:
您正在扩展大量使用原始指针的遗留代码库,并且您希望保持样式的一致性.
您正在使用仅导出原始指针的库,但我猜您仍然可以使用强制转换.
您希望利用指针的功能来提供多个级别的间接.(我不太清楚C++ 11是否足以知道是否可以使用智能指针或使用其他技术来实现.)
您认为哪些其他场景适合使用指针?
你今天甚至会建议学习一般的指针吗?
这个问题旨在成为整数和指针问题之间所有初始化/分配的常见问题解答条目。
我想编写代码,将指针设置为特定的内存地址,例如0x12345678. 但是当用 gcc 编译器编译这段代码时,我得到“初始化使指针从整数而不进行强制转换”警告/错误:
int* p = 0x12345678;
Run Code Online (Sandbox Code Playgroud)
类似地,这段代码给出了“初始化从指针生成整数而不进行强制转换”:
int* p = ...;
int i = p;
Run Code Online (Sandbox Code Playgroud)
如果我在变量声明行之外做同样的事情,消息是一样的,但说的是“赋值”而不是“初始化”:
p = 0x12345678; // "assignment makes pointer from integer without a cast"
i = p; // "assignment makes integer from pointer without a cast"
Run Code Online (Sandbox Code Playgroud)
使用其他流行编译器的测试也会给出错误/警告消息:
int不能用于初始化类型的实体int*”int*在间接级别上与int”不同。问题:上面的例子是有效的 C 吗?
还有一个后续问题:
这不会给出任何警告/错误:
int* p = 0;
Run Code Online (Sandbox Code Playgroud)
为什么不?
int * x;
int v = 7;
Run Code Online (Sandbox Code Playgroud)
鉴于此代码, 1.x = &v和 2.之间有什么区别*x = v?我知道在这两种情况下,都*x包含7但确实x包含v两种情况下的内存位置?如果不是,x案例 1 和案例 2中包含什么,这是两者之间唯一的显着差异吗?
我有一个指向的元素buf: *const T分配开始的指针,并且定义了以下检查:nT
let in_alloc = buf <= ptr && ptr < unsafe { buf.add(n) };
Run Code Online (Sandbox Code Playgroud)
是否保证 是in_alloc的分配中的true任何一个,以及在任何其他情况下?我们可以假设是一个指向对象的有效指针(因此不是未对齐/空/悬挂),但是它可能来自也可能不是来自与 相同的分配。最后我们可以假设大小不为零。ptrbuffalseptrTbufT
考虑下面的 C 代码。我原以为变量bar每次都会被实例化,因此会指向内存中的不同地址,但事实并非如此。
for (i = 2; i < 7; i++) {
struct foo bar;
printf("struct %u\n", bar);
}
Run Code Online (Sandbox Code Playgroud)
输出:
struct 13205520
struct 13205520
struct 13205520
struct 13205520
struct 13205520
Run Code Online (Sandbox Code Playgroud)
如果不明显,我想要的是在 5 个不同的位置生成 5 个不同struct的 s——嗯,实际上是 5 个不同的指向structs 的指针。我怎样才能做到这一点?
在 C 中,a.b通常与 同义(&a)->b。
这绝对在所有情况下都是正确的,即使a是一些杂乱复合术语的宏?或者在任何版本的 C 中是否存在任何边缘情况,其中等价性不成立?
考虑以下代码:
std::map<int, int> m;
int &ref = m[0];
int *ptr = &m[0];
m.insert({1,2});
std::cout << ref; // #1
std::cout << *ptr; // #2
Run Code Online (Sandbox Code Playgroud)
对于像 这样的关联容器std::map,标准说:
insert 和 emplace 成员不应影响迭代器的有效性和对容器的引用,...
这意味着#1绝对没问题。但是,我不太确定#2。
十多年前,这个问题已经被提出和回答。
接受的答案说#2在技术上是不允许的,但在实践中会起作用。
共识答案(赞成票数是接受的答案的两倍多)说#2是可以的,简单地说,上述标准引用暗示指针也没有失效。
这个问题至少还有六个相对较新的副本,其中大多数都有答案,并且所有人都说没问题#2,通常是引用上面相同的标准文本。
我不认为这是正确的。据我了解,引用不是指针,一个不能替代另一个,无论它们是否相互实现。作为对比,下面是标准说有关指称在无序关联容器元素,在换汤不换药的有效性:
重新散列使迭代器无效,...,但不会使对元素的指针或引用无效。
这明确地保证了指针的有效性,表明引用的有效性并不自动暗示它。
那么语言说#2可以吗?它是由 的有效性所暗示的#1吗?
我知道指针(指向数组元素)和迭代器可以递增/递减以遍历元素序列,并且可以在序列中来回跳转元素。
但是如果我增加一个指向单个对象的指针或向它添加一个整数值会发生什么?这是未定义的行为还是可以但我们无法访问该内存?
int x = 551;
int* p = &x;
++p;
--p;
std::cout << *p << '\n';
Run Code Online (Sandbox Code Playgroud)
因为我已经读过我们不应该增加/减少不指向序列或数组中元素的指针。
那么有人可以解释会发生什么以及我的示例是否正常(取消引用指针 p)?谢谢!
我们只能取消引用有效的指针,并且只能检查悬空内置指针指向的地址。我们无法访问它的值(它指向的对象地址中的值)。
int* ptr = nullptr;
if(ptr) // != 0x00000000
std::cout << *ptr << '\n';
ptr = new int(1000);
if(ptr) // != 0x00000000
std::cout << *ptr << '\n';
delete ptr; // still pointing at the address of that dynamic object but that object has been destroyed.
if(ptr) // succeeds or undefined behavior?
std::cout << *ptr << '\n'; // of course UB here
Run Code Online (Sandbox Code Playgroud)
所以我很清楚,但我关心的只是检查指针值是否安全或产生 UB?if(ptr)。因为我们假设我没有像 那样访问该地址中的值std::cout << *ptr。
根据 ISO C(任何版本),指定零大小的数组参数是否有效?
\n该标准似乎含糊不清。虽然很明显零大小的数组是无效的,但数组函数参数很特殊:
\nC23::6.7.6.3/6:
\n\n\n将参数声明为“类型数组”应调整为“指向类型的限定指针”,其中类型限定符(如果有)是在数组类型派生的 [ 和 ] 内指定的限定符。如果\n关键字 static 也出现在数组类型派生的 [ 和 ] 中,则对于每次调用该函数,\n相应实际参数的值应提供对数组的第一个\n元素的访问,\n至少为由 size 表达式指定的许多元素。\n
\n
只要您不使用static,之间指定的大小[]就会被有效忽略。据我了解引用的段落,编译器根本不允许对指针做出任何假设。
那么,下面的代码应该是符合要求的,对吧?
\nvoid h(char *start, char past_end[0]);\n\n#define size 100\nvoid j(void)\n{\n char dst[size];\n h(dst, dst+size);\n}\nRun Code Online (Sandbox Code Playgroud)\n我使用past_end[0]作为指向末尾一位的哨兵指针(而不是大小;在某些情况下它更舒服)。清楚[0]地表明这是一个过去的结束,而不是实际的结束,作为一个指针,读者可能会感到困惑。结束将被标记为end[1]为清楚起见,
GCC 认为它不符合:
\n$ gcc -Wall -Wextra -Wpedantic -pedantic-errors -std=c17 -S ap.c \nap.c:1:26: error: ISO C forbids zero-size array \xe2\x80\x98past_end\xe2\x80\x99 [-Wpedantic]\n 1 | void h(char *start, char …Run Code Online (Sandbox Code Playgroud)