Last Updated: 2024-01-02 11:09:39 Tuesday
-- TOC --
本文及时总结自己终于整明白的C和C++中各种烧脑的定义。
指针的定义:
// C style
char* p = NULL;
char *p = NULL;
// C++ style
char* p = nullptr;
char *p = nullptr;
*
靠近哪边都可以,我习惯写成char *p
,因为可以理解为,*p
结合在一起,表示一个char
,p就是指针,加上*
表示引用指针的值。而且,靠近变量可以更好的支持这种格式:char *p, *q;
。
在C++中,指针应初始化为nullptr。
C++中的引用定义:
string s{"123"};
string& another_s{s};
string &another_s{s};
为了统一习惯,C++的引用,我写成string &s
,ampersand(&)符号与变量名称结合在一起。(这种与变量结合在一起的定义,会带来好处,只有在不需要变量名称的时候,才让*
或&
跟type结合,可以节省一个空格,让代码紧凑一点)
如果在非引用定义的语句中,变量前加
&
,表示使用变量/对象的地址。引用的定义,就是“让我的地址与你一样,我就是和你一样的对象。”
函数指针的定义:
// C style
void (*recv)(mysocket*, int, int, int) = recv_input;
// C++ style
void (*recv)(mysocket*, int, int, int){recv_input};
只有指针可以指向函数,因此指针变量名称前有*
,用()
括起来作为一个整体,()
的优先级高于*
符号,后面再带上一组用括号括起来的参数,最后是赋值。这个值就是一个函数名,编译时,会将这个函数的地址付给指针。
函数指针数组的定义:
// C style
void (*recv[2])(mysocket*, int, int, int) = {recv_input, recv_show};
// C++ style
void (*recv[2])(mysocket*, int, int, int){ recv_input, recv_show };
在函数指针的基础上,增加[]
符号,表示recv变量是个数组,[]
运算符的优先级高于*
符号。*
符号表示这个数组是指针数组,再括起来作为一个整体,再在后面给出参数列表,最后赋初值。
依然保持
*
符号靠近函数指针变量名的习惯!
[]
和()
符号的优先级都排在第1梯队。坑:位运算的优先级很低,一定要加
()
。建议在C++中,尽量使用
{}
来初始化对象,不管是POD对象,还是Class对象,这样可以给自己一点提醒和暗示,表示C++的对象视角。
C++中出现数组引用,好处是无需对引用参数进行是否为nullptr的判断,编译器保证了引用不可能为nullptr。下面是数组引用的定义:
void func(int (&a)[], size_t size);
变量a前面加&
表示引用,用括号括起来作为一个整体,然后是[]
,表示这是个数组。必须要括起来,因为[]
与()
具有相同级别的优先级。
必要的参考学习资料:C语言中的数组和指针
一段测试代码:
#include <cstdio>
using namespace std;
void func1(int (&a)[], size_t size) {
printf("%d\n", a[3]);
}
void func2(int a[], size_t size) {
if (a == nullptr) {
printf("null\n");
return;
}
printf("%d\n", a[3]);
}
void func3(int *a, size_t size) {
func2(a, size);
}
int main(void) {
int a[10]{0,1,2,3,4,5};
func1(a, 10);
func2(a, 10);
func2(nullptr, 10);
func3(a, 10);
return 0;
}
指针指向的内存是const:
const char *p;
char const *p;
指针本身的值是const:
char* const p;
指针的值和指向的内存都是const:
const char* const p;
char const* const p;
const变量可以在runtime期间赋值,赋值后表达readonly含义。
指向一个带\0
的可以用index修改的字符串,空间大小由编译器自动计算:
char a[] = "abcdefg";
指向一个字符数组,末尾没有\0
,可用index修改内容,空间大小由编译器自动计算:
char a[] = {'a','b','c'};
指向一个字符串常量,这个字符串地址不在栈上,内容不可修改,修改指针会导致这个字符串地址丢失,永不可再访问:
char *a = "abcdefg"; // allowed in C
const char *a = "abcdefg"; // C++
不同的类型,就是不同的物种...
// C style
typedef int (*PF)(int, int);
// C++ style is better
using PF = int (*)(int, int);
此时,PF
成为了一个类型,这个类型对应某种确定signature的函数接口。
一段测试代码,同时展示了返回类型为函数指针类型的函数的两种定义方式:
#include <stdio.h>
typedef int (*PF)(int, int);
int add(int a, int b){
return a+b;
}
int subtract(int a, int b){
return a-b;
}
PF get_f(int opt){
if(opt == 0)
return add;
return subtract;
}
int (*get_f2(int opt))(int, int){
if(opt == 0)
return add;
return subtract;
}
int main(void) {
PF f;
f = get_f(0);
printf("%d\n", f(1,2));
printf("%d\n", get_f2(0)(1,2));
f = get_f(1);
printf("%d\n", f(1,2));
printf("%d\n", get_f2(1)(1,2));
return 0;
}
int a[3][4];
int (*b)[4] = a;
int c[2][3][4];
int (*d)[3][4] = c;
(*b)
表达指针,[]
表达数组,4
定义了数组的大小,前面的int
定义了数组元素的类型。变量b
和d
这种指针风格定义的好处是,后面可以直接跟malloc或calloc。
本文链接:https://cs.pynote.net/sf/c/202210291/
-- EOF --
-- MORE --