让我们采用以下函数声明:
void print(SomeType const* i);
Run Code Online (Sandbox Code Playgroud)
在这里,const*参数的性质i暗示了意图,即参数是optional,因为它可能是nullptr。如果这不是故意的,那么参数将只是一个const&. 交流可选的语义当然不是设计指针的初衷,但使用它们来这样做恰好在很长一段时间内都能正常工作。
现在,由于在现代 C++ 中通常不鼓励使用原始指针(并且应该避免使用std::unique_ptr并std::shared_ptr精确指示特定的所有权 -语义),我想知道如何正确指示函数参数的可选 -语义而不传递值,即复制,作为
void print(std::optional<SomeType> i);
Run Code Online (Sandbox Code Playgroud)
会做。
考虑了一段时间后,我想出了使用的想法:
void print(std::optional<SomeType const&> i);
Run Code Online (Sandbox Code Playgroud)
这实际上是最准确的。但事实证明std::optional 不能有引用类型。¹
另外,使用
void print(std::optional<SomeType> const& i);
Run Code Online (Sandbox Code Playgroud)
决不会是最优的,从那时起,我们将要求我们SomeType在向存在std::optional于主叫侧的,可能再次(或相当可能)需要复制那里。
问题:那么在不复制的情况下允许可选参数的现代方法是什么?在这里使用原始指针在现代 C++ 中仍然是一种合理的方法吗?
¹:具有讽刺意味的是,所描述的为什么std::optional不能有引用类型的原因(关于重新绑定或转发赋值的争议)不适用于const引用的std::optionals的情况,因为它们不能被分配。
我如何理解以下复杂的声明?
char (*(*f())[])();
char (*(*X[3])())[5];
void (*f)(int,void (*)());
char far *far *ptr;
typedef void (*pfun)(int,float);
int **(*f)(int**,int**(*)(int **,int **));
Run Code Online (Sandbox Code Playgroud) 是否有可能获得char*一个string在C#中的变量?
我需要将路径字符串转换char*为使用某些原生win32函数...
我正在玩谷歌的Go语言,我遇到了一些在C语言中相当基本的东西,但是到目前为止我看到的文档似乎没有涉及
当我将一个指向一个切片的指针传递给一个函数时,我假设我们有一些方法可以访问它,如下所示:
func conv(x []int, xlen int, h []int, hlen int, y *[]int)
for i := 0; i<xlen; i++ {
for j := 0; j<hlen; j++ {
*y[i+j] += x[i]*h[j]
}
}
}
Run Code Online (Sandbox Code Playgroud)
但Go编译器不喜欢这样:
sean@spray:~/dev$ 8g broke.go
broke.go:8: invalid operation: y[i + j] (index of type *[]int)
Run Code Online (Sandbox Code Playgroud)
足够公平 - 这只是猜测.我有一个相当简单的解决方法:
func conv(x []int, xlen int, h []int, hlen int, y_ *[]int) {
y := *y_
for i := 0; i<xlen; i++ {
for j := 0; j<hlen; j++ {
y[i+j] …Run Code Online (Sandbox Code Playgroud) 当我在C中交换指针时,我一直在努力理解不同的行为.如果我想交换两个int指针,那么我可以做
void intSwap (int *pa, int *pb){
int temp = *pa;
*pa = *pb;
*pb = temp;
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我想交换两个char指针,我需要做类似的事情
void charSwap(char** a, char** b){
char *temp = *a;
*a = *b;
*b = temp;
}
Run Code Online (Sandbox Code Playgroud)
因为如果我这样做
void charSwap(char* a, char* b){
char temp = *a;
*a = *b;
*b = temp;
}
Run Code Online (Sandbox Code Playgroud)
编译器抱怨表达式*a =*b,因为它无法更改值.如果我想交换两个strings(即char* s1= "Hello"; char* s2="Bye";)如何做到这一点?
你能给我一点帮助吗?我想真正了解它是如何工作的,所以在获得正确答案之前我不需要经历反复试验.我希望它对许多其他人有用.
考虑以下代码:
int main() {
int (*p)[]; // pointer to array with unspecified bounds
int a[] = {1};
int b[] = {1,2};
p = &a; // works in C but not in C++
p = &b; // works in C but not in C++
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在纯C中,您可以指定指向任何维度的数组的此类地址的指针.但是在C++中你不能.我发现编译器允许为这样的指针赋值的一种情况:
struct C
{
static int v[];
};
int main()
{
int (*p)[] = &C::v; // works in C++ if 'v' isn't defined (only declared)
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但找不到此代码的任何有用案例.
任何人都可以给出一个有用的示例(在C++中)指向具有未指定边界的数组的指针吗?或者只剩下来自C的遗迹?
考虑以下C程序:
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
Run Code Online (Sandbox Code Playgroud)
根据C11标准,Sec.6.5.16.1,在简单的指配中,"下列之一应持有",列表中唯一相关的一项如下:
左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,左侧指向的类型具有全部右边指出的那种限定词;
此外,这是一个"约束",这意味着,一致的实现必须报告诊断消息(如果它被违反).
在我看来,在上述程序的赋值中违反了这种约束.赋值的两端都是函数指针.所以问题是,两种功能类型是否兼容?第二节回答了这个问题.6.7.6.3:
对于要兼容的两种函数类型,两者都应指定兼容的返回类型.146)此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用中一致; 相应的参数应具有兼容的类型.如果一个类型具有参数类型列表而另一个类型由函数声明符指定,该函数声明符不是函数定义的一部分并且包含空标识符列表,则参数列表不应具有省略号终止符,并且每个参数的类型应为与应用默认参数促销产生的类型兼容.如果一个类型具有参数类型列表而另一个类型由包含(可能为空)标识符列表的函数定义指定,则两者应在参数数量上一致,并且每个原型参数的类型应与类型兼容这是因为默认参数促销应用于相应标识符的类型.
在这种情况下,h1的类型之一具有参数类型列表; 另一个,f,没有.因此,上述引文中的最后一句适用:特别是"两者应在参数数量上达成一致".显然h1需要一个参数.f怎么样?以下几点出现在上述之前:
函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数.
所以很明显f取0个参数.因此,两种类型的参数数量不一致,两种函数类型不兼容,并且赋值违反约束,应该发出诊断.
但是,编译程序时gcc 4.8和Clang都不会发出警告:
tmp$ gcc-mp-4.8 -std=c11 -Wall tmp4.c
tmp$ cc -std=c11 -Wall tmp4.c
tmp$
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果f被声明为"int f(void)...",两个编译器都会发出警告,但根据我对上述标准的阅读,这不应该是必要的.
问题:
Q1:赋值"h1 = f;" 在上面的程序中违反了约束"两个操作数都指向兼容类型的限定或不合格版本"?特别:
Q2:表达式"h1 = f"中的h1类型是某些函数类型T1的指向T1的指针.究竟什么是T1?
Q3:表达式"h1 = f"中的f类型是某些函数类型T2的指向T2的指针.究竟什么是T2?
问题4:T1和T2兼容吗?(请引用标准或其他文件的相应部分以支持答案.)
Q1',Q2',Q3',Q4':现在假设f的声明改为"int f(void){return 9;}".再次回答问题1-4这个程序.
我刚刚在这里阅读了一个有趣的问题,让我想到了另外两件事:
void *或是否持有更丰富的信息(如返回类型,论点和论据类型的号码?)AFAIK,尽管我们无法创建大小为0的静态内存数组,但是我们可以使用动态数组来做到这一点:
int a[0]{}; // Compile-time error
int* p = new int[0]; // Is well-defined
Run Code Online (Sandbox Code Playgroud)
如我所读,p行为就像一个过去的元素。我可以打印p指向的地址。
if(p)
cout << p << endl;
Run Code Online (Sandbox Code Playgroud)
尽管我确定我们不能像使用迭代器(过去元素)那样取消引用该指针(过去元素),但是我不确定是否要增加该指针p?是否像迭代器一样具有未定义的行为(UB)?
p++; // UB?
Run Code Online (Sandbox Code Playgroud)我的一个朋友从“理解和使用 C 指针 - Richard Reese,O'Reilly 出版物”中指出了第二个要点,我无法解释其中的第一句话。我错过了什么?
空指针
指向 void 的指针是一个通用指针,用于保存对任何数据类型的引用。指向 void 的指针的示例如下所示:
Run Code Online (Sandbox Code Playgroud)void *pv;它有两个有趣的特性:
- 指向 void 的指针将具有与指向 的指针相同的表示形式和内存对齐方式
char。- 指向 void 的指针永远不会等于另一个指针。但是,分配了一个
NULL值的两个空指针将是相等的。
这是我的代码,不是书中的代码,所有指针都具有相同的值并且相等。
#include <stdio.h>
int main()
{
int a = 10;
int *p = &a;
void *p1 = (void*)&a;
void *p2 = (void*)&a;
printf("%p %p\n",p1,p2);
printf("%p\n",p);
if(p == p1)
printf("Equal\n");
if(p1 == p2)
printf("Equal\n");
}
Run Code Online (Sandbox Code Playgroud)
输出:
0x7ffe1fbecfec 0x7ffe1fbecfec
0x7ffe1fbecfec
Equal
Equal
Run Code Online (Sandbox Code Playgroud)