c 语言的知识点
参考:
一,
https://www.cnblogs.com/qunqun/p/8653806.html int long long unsigned long long 数据范围
https://blog.csdn.net/lyl0625/article/details/7350045 在C语言中,double、long、unsigned、int、char类型数据所占字节数
三,
https://blog.csdn.net/qq_37867156/article/details/81837545 关于 0x3f3f3f3f
六,
https://blog.csdn.net/qq_34637408/article/details/70877953 二维数组做函数参数 及返回值
七,
https://baike.baidu.com/item/memcpy/659918?fr=aladdin
https://baike.baidu.com/item/memcmp/5494788?fr=aladdin
十,
https://baike.baidu.com/item/%E5%B7%A6%E5%80%BC%E4%B8%8E%E5%8F%B3%E5%80%BC/5537417?fr=aladdin
十一,
https://baike.baidu.com/item/inline/10566989?fr=aladdin inline 关键字
一,数据类型
1,取值范围:
数据类型 | 最小值 | 最大值 | 位数 |
unsigned int | 0 | 4294967295 | |
int | -2147483648 | 2147483647 | 10位 2^31-1 |
unsigned long | 0 | 4294967295 | |
long | -2147483648 | 2147483647 | |
long long | -9223372036854775808 | 9223372036854775807 | 19位 2^63-1 |
unsigned long long | 0 | 18446744073709551615 | 20位 2^64-1 |
__int64 | -9223372036854775808 | 9223372036854775807 | |
unsigned __int64 | 0 | 18446744073709551615 |
2,字节个数(32位编译器下)
char | 1个字节(同64) |
char* | 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器:8个字节) |
short int | 2个字节(同64) |
int | 4个字节(同64) |
unsigned int | 4个字节(同64) |
float | 4个字节(同64) |
double | 8个字节(同64) |
long | 4个字节(同64) |
long long | 8个字节(同64) |
unsigned long | 4个字节(64位的 8个字节) |
二,pi 的定义( 注意定义域)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<math.h> // 三种定义方法 const double pi = acos(-1.0); const double pi = 4 * atan(1.0); const double pi = 2 * asin(1.0); int main(void) { system("pause"); return 0; }
三,int 数据范围下的无穷大的定义
u 作为数字后缀 代表 unsigned 类型变量
>> 1右移一位
综上,在32位的情况下,就是将 32 位的 0 取反后右移一位,也就是 0 + 31个1,即 int 的最大值 2147482347
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> int main(void) { double* p = (double*)malloc(sizeof(double*)); // 这步错了 *p = 100000000000000; printf("%lf\n", *p); free(p); system("pause"); return 0; }
当分配的字节与类型不匹配时,虽然 *p 可以打印出来,但一但用 free 释放就会报错。
这是因为 p 所指向的内存空间的数据类型是 double,占 8 个字节,但却只分配了 4 个字节。
于是,在使用 free() 释放内存的时候就会用 8 个字节的字长去释放 4 个字节的内存空间,导致访问的内存越界。
至于为什么,我给四个字节的空间分配 100000000000000 这么大的数字却没有报错,我就不太清楚了。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> typedef int(*R)[3]; // 列数固定 R fun(int a[][3]) { a[1][3] = 555; return a; } int main(void) { int b[3][3] = { 0 }; R c = fun(b); printf("%d\n", b[1][3]); printf("%d\n", c[1][3]); system("pause"); return 0; }
1,为什么调用 fun 函数之后 main 函数的 数组 b 的值会变呢 ?
其实很简单,其实数组传递的是指针,在函数调用中,它并没有在栈中另开一个数组。而是通过指针引用 main 函数中的数组。之前完全没有想到。( ´◔ ‸◔`)
2,二维数组在函数的形参调用形式,即返回形式应如上述代码。
七,数组的复制与比较
1,memcpy 与 memcmp
头文件:#include<memory.h> 或 #include <string.h>
void *memcpy(void *destin, void *source, unsigned n)
-
-
destin:指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
-
source:指向要复制的数据源,类型强制转换为 void* 指针。
-
n:要被复制的字节数。
- 函数功能:从 source 所指的数据源的起始位置开始拷贝 n 个字节到 destin 中。
- 返回值:返回一个指向 destin 的指针
-
int memcmp(const void *str1, const void *str2, size_t n));
-
- 功能:比较 str1 和 str2 的前 n 个字节。
-
如果返回值 < 0,则表示 str1 小于 str2。
-
如果返回值 > 0,则表示 str2 小于 str1。
-
如果返回值 = 0,则表示 str1 等于 str2。
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<string.h> void show(int a[][2]) { for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) printf("%d ", a[i][j]); puts(""); } } int main(void) { int src[2][2] = { 1,2,3,4 }; int des[2][2] = { 0 }; memcpy(des, src, sizeof(src)); show(des); if (memcmp(src, des, sizeof(src)) == 0) printf("复制成功\n"); else printf("复制失败\n"); system("pause"); return 0; }
int main(void) { char p[] = { 'a','b','c' }; printf("%d\n", strlen(p)); printf("%c\n", *(p + 3)); system("pause"); return 0; }
首先,这段代码是可以正常运行的。其中我得到的 strlen(p) 为 15,但明明 p 数组长度为 3,怎么没有报错。
我猜测原因是:
strlen 会因为没有找到 ‘\0’ 而给 p 数组扩展内存。
验证代码如下(会报错):
int main(void) { char p[] = { 'a','b','c' }; printf("%c\n", *(p + 3)); system("pause"); return 0; }
但有一个问题是:
我在 strlen(p) 后,访问了其后面的空间,还是没有报错,代码如下:
这就把我搞糊涂了。
int main(void) { char p[] = { 'a','b','c' }; printf("%d ", *(p + strlen(p) + 10)); system("pause"); return 0; }
九,#define
1,define 可以用于缩写简单的函数或代码
#define mem(a,b) memset(a,b,sizeof(a))
2,三目运算符的陷阱
#define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> int main(void) { #define MAX(x,y) (x>y)?(x):(y) #define MIN(x,y) (x<y)?(x):(y) printf("%d\n", MIN(3, 4)); printf("%d\n", MIN(11, MAX(3, 4))); system("pause"); }
上述代码看着没什么问题,但是一运行第二个 printf 就结果就错了。为什么会这样呢?
那是因为 #define 只是替换而已,而三目运算符是右结合的,即运算顺序是从右向左算的。
在连用了两个#define 后,如果我们将式子替换回去,就会发现运算顺序乱了。
解决方法,只要在最外面加一个括号保证不会和外界的混淆就可以了。
#define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> int main(void) { #define MAX(x,y) (x>y?x:y) #define MIN(x,y) (x<y?x:y) printf("%d\n", MIN(3, 4)); printf("%d\n", MIN(11, MAX(3, 4))); system("pause"); }
十,左值与右值
十一,inline 关键字
inline 的函数是复制到调用位置,而不是跳转调用,这样的好处是避免函数调用本身出栈入栈消耗额外的时间,而且高速缓存会更容易命中(一项CPU的技术,命中时会提高运行速度)。
inline只用于内容重复,但代码很短的函数,避免出栈入栈消耗额外的时间,其实内联函数并不是真正意义的函数。而是对重复代码的简化。
对于复杂函数,不建议用inline,因为他在每个调用位置都会复制编译,会让代码变得非常长,被100个位置调用,该函数的内存增加100倍,而且现在电脑非常快,inline其实根本没必要,一般只有几行的函数才有理由用inline,因为他的出栈入栈跳转相对本身代码运行时间的比例较高,而长代码就微乎其微。其实 inline 知道有就行,现在编程很少用。用的话这个函数代码也不要超过10行,而且通常C语言会用 宏代码来代替inline完成重复的短代码,宏其实效果比inline更好,这样 inline 使用频率更低, inline用的并不多。
例如:
inline int gcd(int a, int b) { return b ? gcd(b, a%b) : a; } inline int lcm(int a, int b) { return a*b / gcd(a, b); }
十二,
三维数组 a[i] [j] [k] 对应 第 k+1 行,第 j+1 列,第 i+1 层
十三,大小写切换
s[i] ^=32 ,实现大小写的互换,因为英文大小写相应的 ACILL 码值只有 二进制的第 6 位有区别。所以异或上 0010 0000 就实现了大小写互换
#define _CRT_SECURE_NO_WARNINGS #include<stdlib.h> #include<stdio.h> int main(void) { printf("first\n"); printf("second\r"); printf("third\n"); printf("fourth\n\r"); printf("fifth\r\n"); printf("sixth\r\r"); printf("seventh\n\n"); system("pause"); return 0; }
十五,malloc 的用法
int *p = (int*)malloc(int)
解析:
① malloc 里的参数 int 表示请求系统为本程序分配 4 个字节的地址空间
② malloc 函数只能返回第一个字节地址
所以,malloc 只知道第一个字节地址,不知道步长就不知道什么类型,就无法推算出完整的存储空间。所以需要给它步长,即强制转换
③ p 本身内存是静态,p 所指向的内存是动态
注意:
将一个指针作为函数的参数
如果只是改变其指向的地址里的变量值,函数的形参是不需要用 &,因为形参和实参指向的都是同一个地址,且该地址并没有改变 ;
而如果函数里用 malloc 为该形参分配新的地址,则函数的形参需要用 &,因为形参指向的地址被改变,没有和实参指向同一个地址
《坛经》 神秀(唐)
身是菩提树,心如明镜台。
时时勤拂拭,勿使惹尘埃。