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 数据范围下的无穷大的定义

1,0x3f3f3f3f 是一个很有用的数值,它是满足以下两个条件的最大整数。
  ① 它的两倍等于 0x7e7e7e7e,小于 0x7fffffff (int 能表示的最大正整数)。
  ② 它的每 8 位(每个字节)都是相同的。
  关于 ①:
    0x3f3f3f3f 的十进制是 1061109567,是10^9级别的,和0x7fffffff一个数量级。
    所以,它是允许作一次加法运算不溢出的。
  关于 ②:
    memset(a, val, sizeof (a))
      该语句把数值 val(取值范围为 0x00~0xFF)填充到数组a 的每个字节上,所以用 memset 只能得到 “每个字节都相同” 的 int。
      当需要把一个数组中的数值初始化成正无穷时,为了避免加法算术上溢出或者繁琐的判断:
        可以用 memset(a, 0x3f, sizeof(a)) 给数组的每个元素赋成 0x3f3f3f3f
 
2, ~0U >> 1 
  ~ 按位求反
  u 作为数字后缀 代表 unsigned 类型变量
  >> 1右移一位
  综上,在32位的情况下,就是将 32 位的 0 取反后右移一位,也就是 0 + 31个1,即 int 的最大值 2147482347
 
 
 
四,free() 的使用
#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 这么大的数字却没有报错,我就不太清楚了。

 

 

五,~a
  如果 a 为正整数,则其按位取反是
    -a - 1
  如果 a 为负整数,则其按位取反是
    -a - 1
  如果 a == 0,则其按位取反是
     -1
  这是为什么呢?
    条件 ①:
      数据在内存中是用补码表示的。
    条件 ②:
      对于一个 8 位数的二进制 a 进行取反得到的结果为 2- 1 - a
    证:
      设 a 的绝对值的 8 位二进制形式为 x,
      如果 a 为负数,则有:
        a 的原码为:2+ x
        a 的补码为:2+ ( 2- x) = 2- x
        a 的补码进行按位取反为:2- 1 - ( 2- x )) = x - 1
        a 的相反数的原码为:x
        a 的相反数的补码为:x
        所以,有 
          a 的补码进行按位取反 = a 的相反数的补码 x - 1
      如果 a 为正数,则有:
        a 的原码为:x
        a 的补码为:x
        a 的补码进行按位取反为:2- 1 - x
        a 的相反数的原码为:2+ x
        a 的相反数的补码为:2+ ( 2- x) = 2- x
        所以,有 
          a 的补码进行按位取反 = a 的相反数的补码 x - 1
      如果 a 为 0,则有:
        0 的补码为 0000 0000;按位取反为 1111 1111,这是 -1 的补码形式。
        所以,0 按位取反为 -1。
 
 
 
六,二维数组在函数中的使用
#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;
}
 
 
 
八,关于 strlen 的一些问题
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");
}

 

 

十,左值与右值

  左值:L-value,其中 L 指的是 Location,表示可寻址。
  右值:R-value,其中 R 指的是 Read,表示可读。
  左值相当于地址值,右值相当于数据值。
  一个表达式是左值还是还是右值,是根据它的位置来说的,以赋值符号 = 为界,= 左边的就是左值,= 右边就是右值。
  但有的表达式是只能作右值,如:const int a = 5; 中的 a,在如数字:34,它们是无法放在等号左边的。
 
 

十一,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 就实现了大小写互换

 

 

十四,\r\n 的用法
#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 为该形参分配新的地址,则函数的形参需要用 &,因为形参指向的地址被改变,没有和实参指向同一个地址

 

 
 
 
========== ========= ======== ======= ====== ===== ==== === == =

  《坛经》  神秀(唐)

身是菩提树,心如明镜台。

时时勤拂拭,勿使惹尘埃。

 

 

posted @ 2020-02-24 15:03  叫我妖道  阅读(419)  评论(0编辑  收藏  举报
~~加载中~~