字节(Byte): 一个字节由8个相邻的二进制位组成,每个位由0或1组成。字节是计算机技术中最小的可操作存储单位。一个字节的整数范围是0~255

整数 int
int类型的大小。比较常见的是使用4个字节(32位)存储一个int类型的值,但是2个字节(16位)或8个字节(64位)也有可能使用。它们可以表示的整数范围如下。
- 16位:-32,768 到 32,767。
- 32位:-2,147,483,648 到 2,147,483,647。
- 64位:-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
对于int类型,默认是带有正负号的,也就是说int等同于signed int。
int类型表示非负整数时,就必须使用关键字unsigned声明变量。
unsigned int a; //16位的范围为 0到 65535int a; //16位的范围为-32,768 到 32,767。
字符类型char也可以设置signed和unsigned。
signed char c; // 范围为 -128 到 127unsigned char c; // 范围为 0 到 255
字面量后缀
int x = 123;
上面代码中,x是变量,123就是字面量。
编译时,字面量也会写入内存,因此编译器必须为字面量指定数据类型,就像必须为变量指定数据类型一样。
比如,编译器将一个整数字面量指定为int类型,但是程序员希望将其指定为long类型,这时可以为该字面量加上后缀l或L,编译器就知道要把这个字面量的类型指定为long。
int x = 123L;
这里123L写成123l,效果也是一样的,但是建议优先使用L,因为小写的l容易跟数字1混淆。
八进制和十六进制的值,也可以使用后缀l和L指定为 Long 类型,比如020L和0x20L。
如果希望指定为无符号整数unsigned int,可以使用后缀u或U。
int x = 123U;
L和U可以结合使用,表示unsigned long类型。L和U的大小写和组合顺序无所谓。
int x = 123LU;
对于浮点数,编译器默认指定为 double 类型,如果希望指定为其他类型,需要在小数后面添加后缀f(float)或l(long double)。
1.2345e+10F1.2345e+10L
总结一下,常用的字面量后缀有下面这些。
f和F:float类型。l和L:对于整数是long int类型,对于小数是long double类型。ll和LL:Long Long 类型,比如3LL。u和U:表示unsigned int,比如15U、0377U。u还可以与其他整数后缀结合。
溢出
每一种数据类型都有数值范围,如果存放的数值超出了这个范围(小于最小值或大于最大值),需要更多的二进制位存储,就会发生溢出。蹲个屁股,不详细阐述。
一般来说,编译器不会对溢出报错,会正常执行代码,但是会忽略多出来的二进制位,只保留剩下的位,这样往往会得到意想不到的结果。所以,应该避免溢出。
unsigned char x = 255;x = x + 1;printf("%d\n", x); // 0
上面示例中,变量x加1,得到的结果不是256,而是0。因为x是unsign char类型,最大值是255(二进制11111111),加1后就发生了溢出,256(二进制100000000)的最高位1被丢弃,剩下的值就是0。
函数的参数和返回值,
会自动转成函数定义里指定的类型。
int dostuff(int, unsigned char);char m = 42;unsigned short n = 43;long long int c = dostuff(m, n);
上面示例中,参数变量m和n不管原来的类型是什么,都会转成函数dostuff()定义的参数类型。
下面是返回值自动转换类型的例子。
char func(void) {int a = 42;return a;}
上面示例中,函数内部的变量a是int类型,但是返回的值是char类型,因为函数定义中返回的是这个类型。
指针 *
字符*表示指针。指针是什么?首先,它是一个值,这个值(*)代表一个内存地址,因此指针相当于指向某个内存地址的路标。指针变量就是一个普通变量,只不过它的值是内存地址
通常跟在类型关键字的后面,表示指针指向的是什么类型的值。比如,char*表示一个指向字符的指针,float*表示一个指向float类型的值的指针。
int* intPtr;
上面声明了一个变量intPtr,它是一个指针(指针变量?),指向的内存地址存放的是一个整数。
*可以放在变量名与类型关键字之间的任何地方,下面的写法都是有效的。
int *intPtr;int * intPtr;int* intPtr;
* 运算符
*这个符号除了表示指针以外,还可以作为运算符,用来取出指针变量所指向的内存地址里面的值。 *取地址里的值
void increment(int* p) { //指针变量*p = *p + 1; //取值}
上面示例中,函数increment()的参数是一个整数指针p。函数体里面,*p就表示指针p所指向的那个值。对*p赋值,就表示改变指针所指向的那个地址里面的值。
变量地址而不是变量值传入函数,还有一个好处。对于需要大量存储空间的大型变量,复制变量值传入函数,非常浪费时间和空间,不如传入指针来得高效。

& 运算符
& 运算符用来取出一个变量所在的内存地址。&取地址,*取地址里的值
int x = 1;printf("x's address is %p\n", &x);
上面示例中,x是一个整数变量,&x就是x的值所在的内存地址。printf()的%p是内存地址的占位符,可以打印出内存地址。

指针小节中,参数变量加1的函数,可以像下面这样使用。
void increment(int* p) {*p = *p + 1;}int x = 1;increment(&x);printf("%d\n", x); // 2
上面示例中,调用increment()函数以后,变量x的值就增加了1,原因就在于传入函数的是变量x的地址&x。
&运算符与*运算符互为逆运算,下面的表达式总是成立。
int i = 5;if (i == *(&i)) // 正确&取地址,*取地址里的值
例子:



* 的区别:
x 是指针变量,存储的内存地址

*x 是取值操作,获得x(内存地址)上存储的值。

以下这个好像才是标准写法?
