C语言备忘录

概率统计 随机过程 模式识别 信息论
注意1:
#include <stdio.h>
int SQ(int y){
return ((y)*(y));
}
int main(){
int i=1;
printf("%d^2 = %d\n", (i-1), SQ(i++));
return 0;
}

#include <stdio.h>
int SQ(int y){
	return ((y)*(y));
}
int main(){
	int i=1;
	printf("%d^2=%d\n",(i-1),SQ(i++));
	return 0;
}

printf("%d, %d\n", (i-1), SQ(i++));结果为1,1第一个1是因为先经过i =i+1变成2,然后再减1,第二个1是因为i++是先传递再自增。
如果i-1变为i-1,则第一个结果为0。
如果第二个i++,变为i+1,则结果变为4。

一、 C语言中的数组,如 int a[5],int表示数组中是5个整数。
每一个数据叫做数组元素(Element),所包含的数据的个数称为数组长度(Length),例如int a[4];就定义了一个长度为4的整型数组,名字是a。数组中的每个元素都有一个序号,这个序号从0开始,而不是从我们熟悉的1开始,称为下标(Index)。a[0] 表示第0个元素,a[3] 表示第3个元素。
接下来我们就把第一行的4个整数放入数组:
a[0]=20;
a[1]=345;
a[2]=700;
a[3]=22;
这里的0、1、2、3就是数组下标,a[0]、a[1]、a[2]、a[3] 就是数组元素。

二、 就目前学到的知识而言,int、char、float用于scanf函数时都要在前边加上&,而数组和字符串本身就会转换成地址所以不用加&。

三、%c:输出一个字符。c 是 character 的简写。
%s:输出一个字符串。s 是 string 的简写。
%f:输出一个小数。f 是 float 的简写。
%hd用来输出 short int 类型,hd 是 short decimal 的简写;
在现代操作系统中,int 一般占用 4 个字节(Byte)的内存,共计 32 位(Bit)。
%f 以十进制形式输出 float 类型;
%lf 以十进制形式输出 double 类型;

四、sizeof 用来获取某个数据类型或变量所占用的字节数,如果后面跟的是变量名称,那么可以省略( ),如果跟的是数据类型,就必须带上( )。
需要注意的是,sizeof 是C语言中的操作符,不是函数,所以可以不带( )。

五、对插入算法的理解

# include <stdio.h>
int main(void)
{
    int a[23] = {1, 5, 66, 8, 55, 9, 1, 32, 5, 65, 4, 8, 5, 15, 64, 156, 1564, 15, 1, 8, 9, 7, 215};
    int b[24];  //用来存放插入数字后的新数组, 因为又插入了一个值, 所以长度为24
    int Index;  //插入值的下标, Index是“下标”的英文单词
    int num;  //插入的值
    int i;  //循环变量
    printf("请输入插入值的下标:");
    scanf("%d", &Index);
    printf("请输入插入的数值:");
    scanf("%d", &num);
    for (i=0; i<24; ++i)
    {
        if (i < Index)
        {
            b[i] = a[i];  /*循环变量i小于插入值位置Index时, 每一个元素所放的位置不变*/
        }
        else if (i == Index)
        {   
            b[i] = num;  //i等于Index时, 将插入值赋给数组b
        }
        else
        {
            b[i] = a[i-1];  /*因为插入了一个新的元素, 所以插入位置后的每一个元素所存放的位置都要向后移一位*/
        }
    }
    for (i=0; i<24; ++i)
    {
        printf("%d\x20", b[i]);
    }
    printf("\n");
    return 0;
}

①、为什么for循环要循环i<24,因为循环24次,最大的是下标23,而a数组有23个元素,而最大的下标为22,所以为后边 b[i] = a[i-1]减1做准备。
②、插入下标4,就相当于原来的4位置的数往后挪一位,而 b[i] = a[i-1]相当于把挪的哪一位重新再给b数组的位置。删除算法同理。

六、对删除算法的理解

# include <stdio.h>
int main(void)
{
    int a[23] = {1, 5, 66, 8, 55, 9, 1, 32, 5, 65, 4, 8, 5, 15, 64, 156, 1564, 15, 1, 8, 9, 7, 215};
    int b[22];  /*用来存放删除数字后的新数组, 因为删除了一个值, 所以长度为22*/
    int Index;  //要删除的值的下标
    int i;  //循环变量
    printf("请输入要删除的值的下标:");
    scanf("%d", &Index);
    for (i=0; i<23; ++i)
    {
        if (i < Index)
        {
            b[i] = a[i];  /*循环变量i小于插入值位置Index时, 每一个元素所存放的位置不变*/
        }
        else
        {
            b[i] = a[i+1];  /*删除值后面的元素都往前移一位, 要删除的值直接被覆盖*/
        }
    }
    for (i=0; i<22; ++i)
    {
        printf("%d\x20", b[i]);  // \x20表示空格
    }
    printf("\n");
    return 0;
}

①、要把a数组里的数全部循环一遍。
②、 b[i] = a[i+1];相当于在要输出那个下标及之后时用前一个数替换后一个数。

六、对冒泡排序的理解

#include <stdio.h>
int main(){
    int nums[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
    int i, j, temp;
    //冒泡排序算法:进行 n-1 轮比较
    for(i=0; i<10-1; i++){
        //每一轮比较前 n-1-i 个,也就是说,已经排序好的最后 i 个不用比较
        for(j=0; j<10-1-i; j++){
            if(nums[j] > nums[j+1]){
                temp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = temp;
            }
        }
    }
   
    //输出排序后的数组
    for(i=0; i<10; i++){
        printf("%d ", nums[i]);
    }
    printf("\n");
   
    return 0;
}

i=0时,j从0到9-i;两个数换位。
这其实还可以被优化:当比较到第 i 轮的时候,如果剩下的元素已经排序好了,那么就不用再继续比较了,跳出循环即可,这样就减少了比较的次数,提高了执行效率。
未经优化的算法一定会进行 n-1 轮比较,经过优化的算法最多进行 n-1 轮比较,高下立判。
优化后的算法实现如下所示:

#include <stdio.h>
int main(){
    int nums[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
    int i, j, temp, isSorted;
   
    //优化算法:最多进行 n-1 轮比较
    for(i=0; i<10-1; i++){
        isSorted = 1;  //假设剩下的元素已经排序好了
        for(j=0; j<10-1-i; j++){
            if(nums[j] > nums[j+1]){
                temp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = temp;
                isSorted = 0;  //一旦需要交换数组元素,就说明剩下的元素没有排序好
            }
        }
        if(isSorted) break; //如果没有发生交换,说明剩下的元素已经排序好了
    }

    for(i=0; i<10; i++){
        printf("%d ", nums[i]);
    }
    printf("\n");
   
    return 0;
}

我们额外设置了一个变量 isSorted,用它作为标志,值为“真”表示剩下的元素已经排序好了,值为“假”表示剩下的元素还未排序好。
每一轮比较之前,我们预先假设剩下的元素已经排序好了,并将 isSorted 设置为“真”,一旦在比较过程中需要交换元素,就说明假设是错的,剩下的元素没有排序好,于是将 isSorted 的值更改为“假”。
每一轮循环结束后,通过检测 isSorted 的值就知道剩下的元素是否排序好。

六、
i++ 是先引用后增加 ,先在i所在的表达式中使用i的当前值,后让i加1

++i 是先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值

他们其实都是i=i+1的意思,但是在程序中运行的时候的执行的顺序不一样。

七、
程序的全部工作都是由各式各样的函数完成的,函数就好比一个一个的零件,组合在一起构成一台强大的机器。
标准C语言(ANSI C)共定义了15 个头文件,称为“C标准库”,所有的编译器都必须支持,如何正确并熟练的使用这些标准库,可以反映出一个程序员的水平。
合格程序员:<stdio.h>、<ctype.h>、<stdlib.h>、<string.h>
熟练程序员:<assert.h>、<limits.h>、<stddef.h>、<time.h>
优秀程序员:<float.h>、<math.h>、<error.h>、<locale.h>、<setjmp.h>、<signal.h>、<stdarg.h>

八、指针变量
指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据。

#include <stdio.h>
int main(){
    int a = 15;
    int *p = &a;
    printf("%d, %d\n", a, *p);  //两种方式都可以输出a的值
    return 0;
}

运行结果: 15, 15

假设 a 的地址是 0X1000,p 指向 a 后,p 本身的值也会变为 0X1000,*p 表示获取地址 0X1000 上的数据,也即变量 a 的值。从运行结果看,*p 和 a 是等价的。

通过指针交换两个变量的值。

#include <stdio.h>
int main(){
    int a = 100, b = 999, temp;
    int *pa = &a, *pb = &b;
    printf("a=%d, b=%d\n", a, b);
    /*****开始交换*****/
    temp = *pa;  //将a的值先保存起来
    *pa = *pb;  //将b的值交给a
    *pb = temp;  //再将保存起来的a的值交给b
    /*****结束交换*****/
    printf("a=%d, b=%d\n", a, b);
    return 0;
}

运行结果:
a=100, b=999
a=999, b=100

从运行结果可以看出,a、b 的值已经发生了交换。需要注意的是临时变量 temp,它的作用特别重要,因为执行*pa = *pb;语句后 a 的值会被 b 的值覆盖,如果不先将 a 的值保存起来以后就找不到了。

对星号的总结
在我们目前所学到的语法中,星号
主要有三种用途:
①、表示乘法,例如int a = 3, b = 5, c; c = a * b;,这是最容易理解的。
②、表示定义一个指针变量,以和普通变量区分开,例如int a = 100; int *p = &a;。
③、表示获取指针指向的数据,是一种间接操作,例如int a, b, *p = &a; *p = 100; b = *p;。

九、如何以指针的方式遍历数组元素

#include <stdio.h>
int main(){
    int arr[] = { 99, 15, 100, 888, 252 };
    int len = sizeof(arr) / sizeof(int);  //求数组长度
    int i;
    for(i=0; i<len; i++){
        printf("%d  ", *(arr+i) );  //*(arr+i)等价于arr[i]
    }
    printf("\n");
    return 0;
}

运行结果:
99 15 100 888 252

第 5 行代码用来求数组的长度,sizeof(arr) 会获得整个数组所占用的字节数,sizeof(int) 会获得一个数组元素所占用的字节数,它们相除的结果就是数组包含的元素个数,也即数组长度。

第 8 行代码中我们使用了*(arr+i)这个表达式,arr 是数组名,指向数组的第 0 个元素,表示数组首地址, arr+i 指向数组的第 i 个元素,*(arr+i) 表示取第 i 个元素的数据,它等价于 arr[i]。

十、数据结构
①、线性表用于存储具有“一对一”逻辑关系的数据;
②、树结构用于存储具有“一对多”关系的数据;
③、图结构用于存储具有“多对多”关系的数据;

十一、“好”算法的标准
解决一个问题的方法可能有很多,但能称得上算法的,首先它必须能彻底解决这个问题(称为准确性),且根据其编写出的程序在任何情况下都不能崩溃(称为健壮性)。
注意,程序和算法是完全不同的概念。算法是解决某个问题的想法、思路;而程序是在根据算法编写出来的真正可以运行的代码。例如,要依次输出一维数组中的数据元素的值,首先想到的是使用循环结构,在这个算法的基础上,我们才开始编写程序。

在满足准确性和健壮性的基础上,还有一个重要的筛选条件,即通过算法所编写出的程序的运行效率。程序的运行效率具体可以从 2 个方面衡量,分别为:
程序的运行时间。
程序运行所需内存空间的大小。

根据算法编写出的程序,运行时间更短,运行期间占用的内存更少,该算法的运行效率就更高,算法也就更好。

十二、使用无限大的思想”简化频度表达式
在数据结构中,频度表达式可以这样简化:
①、去掉频度表达式中,所有的加法常数式子。例如 2n2+2n+1 简化为 2n2+2n ;
②、如果表达式有多项含有无限大变量的式子,只保留一个拥有指数最高的变量的式子。例如 2n2+2n 简化为 2n2;
③、如果最高项存在系数,且不为 1,直接去掉系数。例如 2n2 系数为 2,直接简化为 n2 ;

十三、通常,每个问题的解决都经过以下两个步骤:
①、分析问题,从问题中提取出有价值的数据,将其存储;
②、对存储的数据进行处理,最终得出问题的答案;

数据结构负责解决第一个问题,即数据的存储问题。通过前面的学习我们知道,针对数据不同的逻辑结构和物理结构,可以选出最优的数据存储结构来存储数据。

而剩下的第二个问题,属于算法的职责范围。算法,从表面意思来理解,即解决问题的方法。我们知道,评价一个算法的好坏,取决于在解决相同问题的前提下,哪种算法的效率最高,而这里的效率指的就是处理数据、分析数据的能力。

因此我们得出这样的结论,数据结构用于解决数据存储问题,而算法用于处理和分析数据,它们是完全不同的两类学科。

也正因为如此,你可以认为数据结构和算法存在“互利共赢、1+1>2”的关系。在解决问题的过程中,数据结构要配合算法选择最优的存储结构来存储数据,而算法也要结合数据存储的特点,用最优的策略来分析并处理数据,由此可以最高效地解决问题。

例如, 有这样一个问题,计算“1+2+3+4+5”的值。这个问题我们可以这样来分析:
计算 1、2、3、4 和 5 的和,首先要选择一种数据存储方式将它们存储起来,通过前面的学习我们知道,数据之间具有“一对一”的逻辑关系,最适合用线性表来存储。结合算法的实现,我们选择顺序表来存储数据(而不是链表)。
接下来,我们选择算法。由于数据集中存放,因此我们可以设计这样一个算法,使用一个初始值为 0 的变量 num 依次同存储的数据做“加”运算,最后得到的新 num 值就是最终结果。

十四、extern
我们知道,程序的编译单位是源程序文件,一个源文件可以包含一个或若干个函数。在函数内定义的变量是局部变量,而在函数之外定义的变量则称为外部变量,外部变量也就是我们所讲的全局变量。它的存储方式为静态存储,其生存周期为整个程序的生存周期。全局变量可以为本文件中的其他函数所共用,它的有效范围为从定义变量的位置开始到本源文件结束。
然而,如果全局变量不在文件的开头定义,有效的作用范围将只限于其定义处到文件结束。如果在定义点之前的函数想引用该全局变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”,表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。

#include <stdio.h>
int max(int x,int y);
int main(void)
{
    int result;
    /*外部变量声明*/
    extern int g_X;
    extern int g_Y;
    result = max(g_X,g_Y);
    printf("the max value is %d\n",result);
    return 0;
}
/*定义两个全局变量*/
int g_X = 10;
int g_Y = 20;
int max(int x, int y)
{
    return (x>y ? x : y);
}

代码中,全局变量 g_X 与 g_Y 是在 main 函数之后声明的,因此它的作用范围不在 main 函数中。如果我们需要在 main 函数中调用它们,就必须使用 extern 来对变量 g_X 与 g_Y 作“外部变量声明”,以扩展全局变量的作用域。也就是说,如果在变量定义之前要使用该变量,则应在使用之前加 extern 声明变量,使作用域扩展到从声明开始到本文件结束。

/****max.c****/
#include <stdio.h>
/*外部变量声明*/
extern int g_X ;
extern int g_Y ;
int max()
{
    return (g_X > g_Y ? g_X : g_Y);
}
/***main.c****/
#include <stdio.h>
/*定义两个全局变量*/
int g_X=10;
int g_Y=20;
int max();
int main(void)
{
    int result;
    result = max();
    printf("the max value is %d\n",result);
    return 0;
}

对于多个文件的工程,都可以采用上面这种方法来操作。对于模块化的程序文件,可在其文件中预先留好外部变量的接口,也就是只采用 extern 声明变量,而不定义变量,max.c 文件中的 g_X 与 g_Y 就是如此操作的。
通常,这些外部变量的接口都是在模块程序的头文件中声明的,当需要使用该模块时,只需要在使用时具体定义一下这些外部变量即可。main.c 里的 g_X 与 g_Y 则是相关示例。
不过,需要特别注意的是,由于用 extern 引用外部变量,可以在引用的模块内修改其变量的值,因此,如果有多个文件同时要对应用的变量进行操作,而且可能会修改该变量,那就会影响其他模块的使用。因此,我们要慎重使用。

十五、char

char i = 0;char i = 'A'%d表示十进制,%c表示单个字符,%x表示十六进制,%s表示字符串 

十六、#include<>和#include""
一种是在包含指令#include后面 ”< >” 将头文件名括起来。这种方式用于标准或系统提供的头文件,到保存系统标准头文件的位置查找头文件。
另一种是在包含指令#include后用双引号 ” ” 将头文件包括起来。这种方式常用与程序员自己的头文件。用这种格式时,C编译器先查找当前目录是否有指定名称的头文件,然后在从标准头文件目录中查找。

十七、准确延时1ms

void Delay1ms(unsigned int y)
{
	unsigned int x;
	for(; y>0; y--)
	{
		for(x = 110; x > 0; x--);
	}
}

十八、C语言中的逻辑判断
①、 a%=b 等效于 a=a%b 模除并赋值。
②、 a|=b 等效于 a=a|b 按位或并赋值。
③、 a&=b 等效于 a=a&b 按位与并赋值。
④、 a^=b 等效于 a=a^b 按位异或并赋值。
⑤、 a!=b 逻辑判断,a不等于b,当ab不等时为真。
⑥、 && 逻辑与,均为真时结果为真。
⑦、 || 逻辑或,均为假时结果为假,否则为真。
⑧、 !a 逻辑非, a为真时结果为假,否则反。
⑨、| 按位或
⑩、^ 按位异或
11、 yi& 按位与
12、 ~ 按位取反

十九、 其中ifndef,是 if not defined 的缩写,其中的define 是一种无参数宏定义。
二十、 short int在32位系统占用2个字节,取值为-32768~32767
unsigned short int在32位系统占用2个字节,取值为0~65535
int、long int、long在32位系统占用4个字节,取值是:-2147483648<---->2147483647
unsigned int在32位系统中占用4个字节,取值是0~4294967295
二十一、 i++ ++i i-- --i
i++ 是先引用后增加 ,先在i所在的表达式中使用i的当前值,后让i加1
++i 是先增加后引用,让i先加1,然后在i所在的表达式中使用i的新值
他们其实都是i=i+1的意思,但是在程序中运行的时候的执行的顺序不一样。
i-- 是先引用后减1 ,先在i所在的表达式中使用i的当前值,后让i减1
–i 是先减1后引用,让i先减1,然后在i所在的表达式中使用i的新值
他们其实都是i=i-1的意思,但是在程序中运行的时候的执行的顺序不一样。
规律:
①、当单独一行语句的时候没有区别。
②、当在表达式中使用时:i++是先取 i 的值做计算,再自增1;++i是先自增,再取i的值做计算。

#include<stdio.h>
int main()
{
	int i = 1;
	int j = 1;
	int k = 1;
	int l = 1;
	
	printf("i:%d,i:%d\n",i++,i);
	printf("j:%d,j:%d\n",++j,j);
	printf("k:%d,k:%d\n",k--,k);
	printf("l:%d,l:%d\n",--l,l);
	
	return 0;
}

i:1,i:2  
j:2,j:2
k:1,k:0
l:0,l:0

#include <iostream>
 
// 函数声明 
void func(void);
 
static int count = 10; /* 全局变量 */
 
int main()
{
    while(count--)
    {
       func();
    }
    return 0;
}
// 函数定义
void func( void )
{
    static int i = 5; // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;
}

当上面的代码被编译和执行时,它会产生下列结果:

变量 i 为 6 , 变量 count 为 9
变量 i 为 7 , 变量 count 为 8
变量 i 为 8 , 变量 count 为 7
变量 i 为 9 , 变量 count 为 6
变量 i 为 10 , 变量 count 为 5
变量 i 为 11 , 变量 count 为 4
变量 i 为 12 , 变量 count 为 3
变量 i 为 13 , 变量 count 为 2
变量 i 为 14 , 变量 count 为 1
变量 i 为 15 , 变量 count 为 0
#include <iostream>
 
// 函数声明 
void func(void);
 
static int count = 10; /* 全局变量 */
 
int main()
{
    while(--count)
    {
       func();
    }
    return 0;
}
// 函数定义
void func( void )
{
    static int i = 5; // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;
}
当上面的代码被编译和执行时,它会产生下列结果:
变量 i 为 6 , 变量 count 为 9
变量 i 为 7 , 变量 count 为 8
变量 i 为 8 , 变量 count 为 7
变量 i 为 9 , 变量 count 为 6
变量 i 为 10 , 变量 count 为 5
变量 i 为 11 , 变量 count 为 4
变量 i 为 12 , 变量 count 为 3
变量 i 为 13 , 变量 count 为 2
变量 i 为 14 , 变量 count 为 1

二十二、break
C++ 和C中中 break 语句有以下两种用法:
①、当 break 语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句。
②、它可用于终止 switch 语句中的一个 case。
for和while是循环,而if不是循环,切记!

#include <iostream>
using namespace std;
 
int main ()
{
   // 局部变量声明
   int a = 10;

   // do 循环执行
   do
   {
       cout << "a 的值:" << a << endl;
       a = a + 1;
       if( a > 15)
       {
          // 终止循环
          break;
       }
   }while( a < 20 );
 
   return 0;
}

二十三、continue
continue是跳出这一次循环,剩下的不执行,而重新判断执行下一次循环。

   int a = 0;
    while (1) {
         a++;
        printf("%d\n",a);
        if(a==5){
            continue;
        }
        printf("测试\n");
        if(a==10){
            break;
        }
    }

结果:
1
测试
2
测试
3
测试
4
测试
5
6
测试
7
测试
8
测试
9
测试
10
测试

二十四、goto
goto 语句允许把控制无条件转移到同一函数内的被标记的语句。
goto label;

.
label: statement;
在这里,label 是识别被标记语句的标识符,可以是任何除 C++ 关键字以外的纯文本。标记语句可以是任何语句,放置在标识符和冒号(:)后边。

#include <iostream>
using namespace std;
 
int main ()
{
   // 局部变量声明
   int a = 10;

   // do 循环执行
   LOOP:do
   {
       if( a == 15)
       {
          // 跳过迭代
          a = a + 1;
          goto LOOP;
       }
       cout << "a 的值:" << a << endl;
       a = a + 1;
   }while( a < 20 );
 
   return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

a 的值: 10
a 的值: 11
a 的值: 12
a 的值: 13
a 的值: 14
a 的值: 16
a 的值: 17
a 的值: 18
a 的值: 19

二十五、 ①、%n.mf的打印格式表达意思如下:
②、首先%f是打印实数数据(float double类型的数据)。
③、n表示有效数字个数,m表示小数点后的位数。

二十六、 C语言类型声明是从外到里解方程,把右边看做是表达式!

char *(*(*a)(void))[20]中,
*(*(*a)(void))[20]是一个char
所以(*(*a)(void))[20]是一个char*
所以*(*a)(void)是一个char*的数组,有20个元素
所以(*a)(void)是一个指向20char*的数组的指针
所以*a是无参数并返回指向20char*的数组的指针的函数
所以a是无参数并返回指向20char*的数组的指针的函数的指针

1. []()具有相同优先级,比*的优先级高;
2.相同优先级,从左到右结合。

二十七、 for
for 循环语句的一般形式为:
for (表达式1; 表达式2; 表达式3)
{
语句;
}

首先要强调两点:
下面来看看它的执行过程:
①、求解表达式1。
②、求解表达式2。若其值为真,则执行 for 语句中指定的内嵌语句,然后执行第3步;若表达式2值为假,则结束循环,转到第5步。
③、求解表达式3。
④、转回上面第2步继续执行。
⑤、循环结束,执行 for 语句下面的语句。

二十八、const
使用const的一些建议
1 要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;
2 要避免最一般的赋值操作错误,如将const变量赋值;
3 在参数中使用const应该使用引用或指针,而不是一般的对象实例;
4 const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;
5 不要轻易的将函数的返回值类型定为const;
6除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;

二十九、低字节、高字节
FLASH闪存bai 闪存的英文名称是du"Flash Memory",一般简称为"Flash",它属于内存器zhi件的一种,dao是一种不挥发zhuan性( Non-Volatile )内存。闪存的物理特性与shu常见的内存有根本性的差异:目前各类 DDR 、 SDRAM 或者 RDRAM 都属于挥发性内存,只要停止电流供应内存中的数据便无法保持,因此每次电脑开机都需要把数据重新载入内存;闪存在没有电流供应的条件下也能够长久地保持数据,其存储特性相当于硬盘,这项特性正是闪存得以成为各类便携型数字设备的存储介质的基础。

三十、进制问题
C/C++规定,16进制数必须以 0x开头。比如 0x1表示一个16进制数。而1则表示一个十进制。另外如:0xff,0xFF,0X102A,等等.其中的x不用区分大小写(注意:0x中的0是数字0,而不是字母O)。此外,C/C++中,10进制数有正负之分。比如12表示正12,而-12表示负12。但8进制和16进制只能表达无符号的正整数,如果在代码中写-0xF2,C/C++并不把它当成一个负数。

三十一、指针变量的加减
指针变量的加减,以指针所指向的类型空间为单位进行偏移。

	int a[5] = { 1,3,5,7,9 };
	int* p = a;
	printf("a:%p\n", a);
	printf("a+1:%p\n", a + 1);
	printf("&a:%p\n", &a);
	printf("&a+1:%p\n", &a + 1);
	/*
		a指向的是a[0],它的类型是int *
		&a指向的是整个数组,它的类型是int(*)[5];
		a与&a所存放的内容都是a的首地址,但它们所指向的内容不一样,即对指针变量的加减来说也不一样,
		a+1是a的首地址的下一个地址,即首地址加4,而&a+1是指首地址加上整个元素的数量,即加20.
	
	/*

三十三、sizeof

#include<stdio.h>
#include<string.h>
int main(void)
{
	int a[] = { 1,2,3,4,5,6,7,8,9 };
	int n1 = 0;
	int n2 = 0;
	n1 = sizeof(a) / sizeof(a[0]);
	n2 = sizeof(a) ;
	printf("n1 = %d\n", n1);
	printf("n2 = %d\n", n2);
	return 0;
}

sizeof求的是占用的是内存数。

三十三、strlen

#include <stdio.h>
#include <string.h>
int main() {
    char str[100] = { 0 };
    size_t len;
    gets_s(str);
    len = strlen(str);
    printf("Length: %d\n", len);
    return 0;
}

#include <stdio.h>
#include <string.h>
int main() {
    char str1[] = "Iam";
    char str2[] = { 'I', 'a', 'm' };
    int len1, len2;
    len1 = sizeof(str1);
    len2 = sizeof(str2);
    printf("sizeof(str1): %d\n", len1);
    printf("sizeof(str2): %d\n", len2);
    return 0;
}

当用sizeof时,像"Iam"这种字符包括’\0’,所以占4个字节,而{ ‘I’, ‘a’, ‘m’ }不包括’\0’,占用3个字节。

#include <stdio.h>
#include <string.h>
int main() {
    char str1[] = "Iam";
    char str2[] = { 'I', 'a', 'm' , '\0'};
    int len1, len2;
    len1 = strlen(str1);
    len2 = strlen(str2);
    printf("strlen(str1): %d\n", len1);
    printf("strlen(str2): %d\n", len2);
    return 0;
}

当用strlen时,像"Iam"这种字符不包括’\0\,占用3个字节,而而{ ‘I’, ‘a’, ‘m’ }不包括’\0’,占用3个字节。
三十四、两个指针相减结果为步长单位

#include <stdio.h>
#include <stdlib.h>
#include <String.h>

int main()
{
	int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int* p2 = &a[2]; //第3个元素地址
	int* p1 = &a[1]; //第2个元素地址

	printf("p1 = %p, p2 = %p\n", p1, p2);//p1 = 012FFE58, p2 = 012FFE5C

	int n1 = p2 - p1;
	int n2 = (int)p2 - (int)p1; 

	printf("n1 = %d, n2 = %d\n", n1, n2);//n1 = 1, n2 = 4
	//注意:两个指针相减结果为步长单位

	return 0;
}

指针步长:一个地址操作一个字节,地址加一就代表操作的字节加一,步长由指针指向的地址数据类型大小决定,比如int类型指针+1,地址就要加四个字节(地址+4),char类型指针+1,地址就要加1个字节(地址+1)

三十五:p++ 、(p+1)、*(p+1)

#include <stdio.h>

int main(void)
{
	int arr[5] = { 10, 20, 30, 40, 50 };
	int* p = arr ;
	//优先级++比*高
	//i++ --> i = i + 1		++在右边先使用后加减
	//*p++ --> *(p = p + 1),根据++在左边先加减后使用的原则,因此返回*P,结果*p = *(p+1)
	printf("*p++:%d\n", *p++);//10,p = p + 1
	printf("*p:%d\n", *p);
	//(*p)++ --> (*p) = (*p) + 1 根据++在右边先使用后加减的原则,因此返回20,*p = *p + 1 = 21
	printf("(*p)++:%d\n", (*p)++);//20, *p = 21
	printf("*p:%d\n", *p);
	//*(p++) --> *p = *(p+1),根据++在右边先使用后加减的原则,因此返回21,*p = *(p+1) = 30
	printf("*(p++):%d\n", *(p++));//21, *p = 30
	printf("*p:%d\n", *p);
	return 0;
}

三十六、数组指针与指针数组

#include <stdio.h>

void test1()
{
	int num1 = 10;
	int num2 = 20;
	int num3 = 30;
	int* arr[3] = { &num1, &num2, &num3 };
	char* brr[3];
	short* crr[3];

	int drr[3];
	char err[3];
	short frr[3];
	//arr[0] = &num1 arr[1] = &num2 arr[2] = &num3
	//*arr[0] = num1 *arr[1] = num2 *arr[2] = num3
	//*arr[0]是arr第0个元素的地址	*arr[1]是arr第1个元素的地址	*arr[2]是arr第2个元素的地址
	printf("sizeof(arr) = %d\n", sizeof(arr));//int*表示arr数组存储的是地址,sizeof(数组名)是计算的是整个数组的大小:数组类型(由于存放的是地址,故每个元素都是Int类型的)*数组元素个数
	printf("sizeof(brr) = %d\n", sizeof(brr));
	printf("sizeof(crr) = %d\n", sizeof(crr));
	printf("sizeof(drr) = %d\n", sizeof(drr));
	printf("sizeof(err) = %d\n", sizeof(err));
	printf("sizeof(frr) = %d\n", sizeof(frr));

	printf("sizeof(&arr) = %d\n", sizeof(&arr));
	printf("sizeof(&brr) = %d\n", sizeof(&brr));
	printf("sizeof(&crr) = %d\n", sizeof(&crr));
	printf("*arr[0] = %d\n", *arr[0]);
	printf("*arr[1] = %d\n", *arr[1]);
	printf("*arr[2] = %d\n", *arr[2]);

	printf("arr[0] = %p\n", arr[0]);
	printf("arr[1] = %p\n", arr[1]);
	printf("arr[2] = %p\n", arr[2]);

	printf("num1 = %p\n", &num1);
	printf("num2 = %p\n", &num2);
	printf("num3 = %p\n", &num3);

	printf("&arr[0] = %p\n", &arr[0]);
	printf("&arr[1] = %p\n", &arr[1]);
	printf("&arr[2] = %p\n", &arr[2]);
}

void test2()
{
	int brr[3] = { 0 };
	printf("sizeof(brr) = %d\n", sizeof(brr));

}

void test3()
{
	char* crr[3];
	char drr[3];
	printf("sizeof(crr) = %d\n", sizeof(crr));//数组占的空间 = 数组元素 * 数组的类型大小;即sizeof(crr) = 3 * (char*) = 3 * 4 = 12
	printf("sizeof(drr) = %d\n", sizeof(drr));//sizeof(drr) = 3 * 1 = 3
}

void test4()
{

	const char* err[3] = { "hahaha", "hehehe", "heiheihei" };

	//数组指针,本质是一个指针,只不过指针存的是数组
	//指针数组,本质上是数组,只不过数组里存的是指针
	//sizeof(),数组指针的内存是4字节,因为32位系统本质上的地址空间都是一样的,占用4个字节;数组指针的占用的内存与数组= 数组的数量 * 数组的类型大小
	//err数组保存的是三个字符串的地址,而不是它的字符串

	printf("err[1] = %s\n", err[1]);
	printf("err[1]中的第二个元素 = %c\n", *(err[1] + 2));
}

void test5()
{
	int arr[5] = { 10, 20, 30, 40, 50 };
	int(*p)[5] ;//p的五个数组元素存放的都是arr各个数组的地址,数组指针是指本质上是指针,存放的是数组,而数组的每个元素都是地址,因此*p是指向每个元素的地址,p是自己的地址,**p是指向的元素内容
	//;指针数组是指本质上是数组,存放的是指针
	//int(*p)[5] 于 int *p类比,因此*p是指
	p = &arr;
	printf("*p:%p\n", *p );
	printf("*p + 1:%p\n", *p + 1);
	printf("*p + 2:%p\n", *p + 2);
	printf("*p + 3:%p\n", *p + 3);
	printf("*p + 4:%p\n", *p + 4);

	printf("&arr[0]:%p\n", &arr[0]);
	printf("&arr[1]:%p\n", &arr[1]);
	printf("&arr[2]:%p\n", &arr[2]);
	printf("&arr[3]:%p\n", &arr[3]);
	printf("&arr[4]:%p\n", &arr[4]);

	printf("*(*p+3):%d\n", *(*p + 3));
	printf("p:%p\n", p);
	printf("arr:%p\n", arr);
	printf("**p:%d\n", **p);
}

void test6()
{
	int arr[5] = { 10, 20, 30, 40, 50 };
	printf("arr+1:%p\n", arr+1);
	printf("&arr + 1:%p\n", &arr + 1);
	//int* p = arr;//p = arr, arr指的是arr数组的第零个元素的地址
	int* p = NULL;
	p = (int*)&arr;//p只与自身定义的的数据类型有关
	printf("p:%p\n", p);
	printf("arr:%p\n", arr);
	printf("p+1:%p\n", p+1);
	printf("arr+1:%p\n", arr+1);

	printf("*(p+1):%d\n", *(p+1));
	printf("p[0]+1:%d\n", p[0]+1);
	printf("p[1]:%d\n", p[1]);
	printf("p[2]:%d\n", p[2]);
	printf("p[3]:%d\n", p[3]);
	printf("p[4]:%d\n", p[4]);
}
int main(void)
{
	test5();
	return 0;
}

三十七、数组指针与函数指针诀窍
1、数组名作为类型时代表的是数组的总大小,如一级数组sizeof(数组名);
2、数组名作为作为地址时代表的是首元素的地址,如一级数组int *p = 数组名;
3、如果想在函数内部修改外部变量的值,就需要把外部变量的地址传递给函数,(以指针变量作为函数的参数)。

三十八、%#x
1,格式控制符“%p”中的p是pointer(指针)的缩写。指针的值是语言实现(编译程序)相关的,但几乎所有实现中,指针的值都是一个表示地址空间中某个存储器单元的整数。printf函数族中对于%p一般以十六进制整数方式输出指针的值,附加前缀0x。

2,这里的"c=%#x\n"意思是:是一个格式控制符,其中c=是普通字符,%#x是格式说明,\n是转义字符;其中的%#表示的输出提示方式,如果是8进制,在前面加0,如果是十进制,不加任何字符,如果是十六进制,会加上0x

三十九、计算机存储
计算机存储:最小单位是字节,存储顺序(小端模式)为低位字节在前、高位在后。
而一个十六进制的表示,如0x01020304;左边的就是高位,右边的就是低位。

四十、C语言^、~
^:按位异或
~:按位取反

四十一、看门狗
看门狗就像一个定时器一样。
应用:一个应用会是一个死循环则一直在运行,然后要在程序运行的某个时刻“喂狗”,也就是给看门狗的计数值赋一个初始值,防止计数值减到0。
如果一个应用程序“跑飞”了,那么它就会脱离这个应用的死循环,然后就不会再继续“喂狗”了,这样计数值会见到0.触发中断或者重启。

四十二、看门狗和中断
看门狗清零语句绝对不能放在中断语句中!因为中断服务程序的执行是有条件的,如果一定时间内不满足条件,中断就不会发生,而看门狗就会超时,引起不必要的复位。
如果程序跑飞了死循环在某处,但中断条件还有效,那么如果中断条件一直满足,中断程序就会被执行,如果这个执行间隔小于看门狗计时周期,看门狗就失去作用了。
注意:但是如果一个中段的执行时长超过了清看门狗的时间 没有清看门狗指令那岂不是会导致复位
因此,是这样的,所以取看门狗时限要留有余地。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值