C语言个人学习笔记(不定时查漏补缺修改更新)

目录

数据类型/数据存储

       数据类型 

所表达数的范围:char

内存中所占据的大小:1个字节到16个字节

各类型所占大小

大小端

反码,原码,补码

指针

定义指针

指针的操作

野指针

字符指针

指针与数组

指针数组

 一般使用

数组指针

一个数组指针的使用 

指针的应用

传递参数

传递返回值

直接访问物理地址下的数据




数据类型/数据存储


       数据类型 

所表达数的范围:char<short<int<float<double
内存中所占据的大小:1个字节到16个字节

内存中表达形式:整型:         二进制数(补码)

                            浮点数:         编码 


  • 各类型所占大小

  1. int 4个字节(1个字节8个比特【bit】) int=32位变量 比特    {取决于cpu 通常1字长 取决于cpu   }   
  2. double 8个字节 64位bit           双精度浮点数       doublefloat精度高
  3. double 8个字节 64位bit 
  4. long double 16字节
  5. char 1字节 -128~127
  6. short 2字节 -32768~32767
  7. long 8字节(64位8字节     32位4字节) { 取决于cpu 通常1字长 取决于cpu   }

        字长:32bit或64bit( 取决于cpu内的寄存器)

大小端

  1. 大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中
  2. 小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,   保存在内存的高地址中

        现在的电脑通常都用小端模式存储

反码,原码,补码

原码:直接将数值按照正负数的形式翻译成二进制就可以得到原码。

反码:将原码的符号位不变,其他位依次按位取反就可以得到反码

补码:反码+1就得到补码。

对于整形来说:数据存放内存中其实存放的是补码。

为什么呢?

在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;

同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。




指针

定义指针

指针的本质就是地址

//口语中的指针,其实是指针变量;指针变量就是一个变量,指针变量是用来存放的一个变量

用于存放其他数据单元(变量/数组/结构体/函数等)的首地址

数据类型

指向该数据类型的指针

(unsigned) char

1字节

(unsigned) char *

x字节

(unsigned) short

2字节

(unsigned) short *

x字节

(unsigned) int

4字节

(unsigned) int *

x字节

(unsigned) long

4字节

(unsigned) long *

x字节

float

4字节

float *

x字节

double

8字节

double *

x字节

指针的大小:16位系统:x=2,32位系统:x=4,64位系统:x=8

#include <stdio.h>
int main()
{
    char a = 0x66;
    char* p;\\定义一个指向字符的指针变量,大小与系统的架构相关,但与其指向的数据类型无关。

    p = &a;                     //把a的地址赋值给p
 
    printf("%x\n",a);           //输入结果:66   说明是a的值
    printf("%x\n",p);           //输入结果:62fe47 说明是p的地址
    printf("%x\n",*p);          //输入结果:66     取出指针指向的数据单元,指针指向P,
                                //P中存储的是a的地址,通过a的地址找到a的内容,把a的内容输出
 
    return 0;
}

P++与P--的说明

#include <stdio.h>
int main()
{
    int a = 0x66;
    int* p;
    p = &a;                     //把a的地址赋值给p
 
    printf("%x\n",a);           //输出结果:66   说明是a的值
    printf("%x\n",p);           //输出结果:62fe47 说明是p的地址
    printf("%x\n",*p);          //输出结果:66     取出指针指向的数据单元,指针指向P,
                                //P中存储的是a的地址,通过a的地址找到a的内容,把a的内容输出
    p++;
    printf("%x\n",p);           //输出结果:62fe48 说明指针p++,使指针向下移动1个数据宽度。
                                //char类型1字节,int4字节
    return 0;
}

指针的操作

已定义指针

                        int a

int *p

定义一个指向int型数据的指针

操作方式

举例

解释

取地址

p=&a

将数据a的首地址赋值给p

取内容

*p;

取出指针指向的数据单元

p++;

使指针向下移动1个数据宽度

p=p+5;

使指针向下移动5个数据宽度

p--

使指针向上移动1个数据宽度

p=p-5;

使指针向上移动5个数据宽度

指针类型决定了指针在被解引用的时候访问了几个字节

指针类型决定了+1 -1操作跳过几个字节


野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

1. 指针未初始化

2.越界

字符指针

本质是把字符串 hello bit. 首字符的地址放到了指针中。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{

const char* p1="abcdef";
const char* p2="abcdef";
char arr1[]="abcdef";
char arr2[]="abcdef";
char arr3[]={1};
char arr4[]={1};
if(p1==p2)
{
    printf("p1==p2\n");
}
else
{
    printf("p1!=p2\n");
}
if(arr1==arr2)
{
    printf("arr1==arr2\n");
}
else
{
    printf("arr1!=arr2\n");
}
if(arr3==arr4)
{
    printf("yes\n");
}
else
{
    printf("no\n");
}


}

C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。

但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。

***arr数组比较比的是地址相同不相同

指针与数组

数组是一些相同数据类型的变量组成的集合,其数组名即为指向该数据类型的指针数组的定义等效于申请内存、定义指针和初始化

数组名表示数组首元素的地址

例如:     char c[ ] = {0x33, 0x34, 0x35};

等效于:    申请内存

        定义 char *c = 0x4000;

        初始化数组数据

利用下标引用数组数据也等效于指针取内容

例如:    c[0];    等效于:    *c;

        c[1];    等效于:    *(c+1);

        c[2];    等效于:    *(c+2);

同级指针之间才能相互赋值,跨级赋值将会导致编译器报错或警告

#include <stdio.h>
int main()
{
    char a[] = {0x33,0x34,0x35};
    char* p;

    p = a;
 
    printf("%x\n", a[0]);               //输出结果:33
    printf("%x\n", a[1]);               //输出结果:34
    printf("%x\n", a[2]);               //输出结果:35
 
    printf("\n");
 
    printf("%x\n", *p);                 //输出结果:33
    printf("%x\n", *(p+1));             //输出结果:34
    printf("%x\n", *(p + 2));           //输出结果:35
 
    /*说明
    数组取下标,就是指针取内容的表示形态
    *p打印流程:
    1.P中存储的是数组a
    2.数组a的地址内容是{0x33,0x34,0x35}
    */
    return 0;
}

第二种形态

第二种形态
#include <stdio.h>
#include <stdlib.h>
int main()
{
    
    int* a;
    a = malloc(3 * 4);                  //表示3个变量,变量为4个字节大小
    *a = 0x33;
    *(a + 1) = 0x34;
    *(a + 2) = 0x35;
                                //等同于int a【】数组
    /*
    1.引用指针
    2.申请内存
    3.初始化
    */
 
    printf("%x\n", a[0]);               //输出结果:33
    printf("%x\n", a[1]);               //输出结果:34
    printf("%x\n", a[2]);               //输出结果:35
 
    printf("\n");
 
    printf("%x\n", *p);                 //输出结果:33
    printf("%x\n", *(p+1));             //输出结果:34
    printf("%x\n", *(p + 2));           //输出结果:35
 
    /*说明
    数组取下标,就是指针取内容的表示形态
    
    *p打印流程:
    1.P中存储的是数组a
    2.数组a的地址内容是{0x33,0x34,0x35}
    */
 
    return 0;
}

指针数组

指针数组重点是数组--------------用来存放指针的数组

Int* arr[10]; //存放整形指针的数组

Char* arr3[5];//存放字符指针的数组

int arr1[]={1,2,3,4};
int arr2[]={2,3,4,5,6};
int arr3[]={3,4,5,6};

int* parr[3]={arr1,arr2,arr3};
return 0;

 一般使用

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col) {
int i = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", arr[i][j]);
} printf("\n");
} }
void print_arr2(int (*arr)[5], int row, int col) {
int i = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", arr[i][j]); }
printf("\n"); }
} int main() {
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10}; print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收
print_arr2(arr, 3, 5);
 return 0;
}

数组指针

指向数组的指针

#include <stdio.h>
int main() {
int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p //但是我们一般很少这样写代码
return 0;
} 

一个数组指针的使用 

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col) {
int i = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", arr[i][j]);
} printf("\n");
} }
void print_arr2(int (*arr)[5], int row, int col) {
int i = 0;
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
printf("%d ", arr[i][j]); }
printf("\n"); }
} int main() {
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10}; print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址//可以数组指针来接收
print_arr2(arr, 3, 5); 
return 0;
}


指针的应用

  • 传递参数

  1. 使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用
  2. 使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计
  1. 使用指针传递大容量的参数,主函数和子函数使用的是同一套数据,避免了参数传递过程中的数据复制,提高了运行效率,减少了内存占用
	#include <stdio.h>
	 
	int FindMax(int *array,int count)
	{
	    int i;
	    int max = array[0];
	    for(i=0;i<count;i++)
	    {
	        if(array[i]>max)
	        {
	            max = array[i];
	        }
	    }
	    return max;
	}
	int main(void)
	{
	    int a[] = {1,2,3,5,4,3};
	    int Max;
	    Max = FindMax(a,6);
	    printf("the max is %d\n",Max);
	    
	    return 0;
	}

1. #include <stdio.h> 导入标准输入输出头文件。

2. 定义函数FindMax,参数是int指针array和元素个数count。

3. 在函数内部,通过array指针可以访问数组内容。

4. 使用for循环和if条件判断,找出数组中的最大值。

5. main函数中定义了数组a并初始化。

6. 调用FindMax函数,传入数组a和元素个数。

7. FindMax通过指针访问数组,比较找最大值。

8. 将返回的最大值存储在Max变量中。

9. 最后打印出Max。


2.使用指针传递输出参数,利用主函数和子函数使用同一套数据的特性,实现数据的返回,可实现多返回值函数的设计

#include <stdio.h>
 
void FindMaxAndcount(int *max,int *count,const int *array,int length)
{
    
    int i;
    *max = array[0];
    *count = 1;
    for(i = 1;i < length;i++)
    {
        if(*max < array[i])
        {
            *max = array[i];
            *count = 1;
        }
        else if(*max == array[i])
        {
            *count = *count + 1;
        }
    } 
   
}
int main(void)
{
    int a[] = {13,2,3,5,4,3};
    int Max;
    int count;
    FindMaxAndcount(&Max,&count,a,sizeof(a)/sizeof(a[0]));
    printf("the Max is %d\n",Max);
    printf("the count is %d\n",count);
    
    return 0;
}

1. 定义函数FindMaxAndCount,参数有:  - 最大值的指针max  - 次数的指针count  - 数组的指针array  - 数组长度length

2. 在函数中,通过array指针访问数组元素。

3. 使用循环和比较找出最大值,存入*max。

4. 同时统计最大值出现的次数,存入*count。

5. main函数定义数组a,调用函数并传入参数:  - &Max - 最大值的指针  - &count - 次数的指针  - a - 数组  - 数组长度

6. 函数执行后,Max和count通过指针返回结果。

7. 打印出最大值和次数。


  • 传递返回值

将模块内的公有部分返回,让主函数持有模块的“句柄”,便于程序对指定对象的操作

#include <stdio.h>
/***********************/
 int Time[]={23,59,55};
 int *GetTime(void)
 {
     return Time;
 }
 /***********************/
 
int main(void)
{
    int *p;
    p = GetTime();
    printf("the time is %d:%d:%d\n",*p,*(p+1),*(p+2));
    return 0;
}

1. 定义一个全局数组Time[],用来存储时间。

2. 定义函数GetTime(),该函数返回Time数组的指针。

3. main函数中,调用GetTime(),将返回的指针存入p

4. 通过指针p可以直接访问Time数组的元素。

5. 使用pp+1p+2来依次访问Time的每个元素。

6. 打印出获取的时间。


  • 直接访问物理地址下的数据

访问硬件指定内存下的数据,如设备ID号等

将复杂格式的数据转换为字节,方便通信与存储

1.访问硬件指定内存下的数据,如设备ID号等 

#include <stdio.h>
/***********************/
unsigned char AirDate[20];
 void SendDate(const unsigned char *date,unsigned char count)
 {
    unsigned char i;
    for(i=0;i<count;i++)
    {
        AirDate[i] = date[i];
    }
     
 }  
 /***********************/
 int main(void)
 {
    unsigned char i;
    unsigned char DateSend[]={0x01,0x02,0x03,0x04,0x05};
    SendDate(DateSend,5);
 
 /***********************/
 printf("\nAirDate=");
    for(i=0;i<5;i++)
    {
        printf("%x",AirDate[i]);
    }
 /***********************/ 
   
 void ReceiveDate(unsigned char *date,unsigned char count)
    {
        unsigned char i;
        for(i=0;i<count;i++)
        {
            date[i] = AirDate[i];
        }
    }
    unsigned char DateReceive[20];

    ReceiveDate(DateReceive,5);

    printf("\nDateReceive=");

    for(i=0;i<5;i++)
    {
        printf("%x",DateReceive[i]);
    }
    printf("\n");
    return 0;
 }

1. 定义全局数组AirDate来模拟空中数据。

2. 定义函数SendDate,通过指针参数date发送数据。

3. main函数中初始化数据数组DateSend,调用SendDate发送。

4. SendDate通过指针拷贝DateSend到AirDate。

5. 定义函数ReceiveData通过指针参数date来接收数据。

6. main函数中定义数组DateReceive,调用ReceiveData接收数据。

7. ReceiveData通过指针从AirDate拷贝到DateReceive。


 2.将复杂格式的数据转换为字节,方便通信与存储(模拟串口)

	#include <stdio.h>
	/***********************/
	unsigned char AirDate[20];
	 void SendDate(const unsigned char *date,unsigned char count)
	 {
	    unsigned char i;
	    for(i=0;i<count;i++)
	    {
	        AirDate[i] = date[i];
	    }
	     
	 }  
	 /***********************/
	 int main(void)
	 {
	    unsigned char i;
	    float num=12.345;
	    unsigned char *p;
	    p = (unsigned char *)&num;
	    SendDate(p,4);
	 
	 /***********************/
	 printf("\nAirDate=");
	    for(i=0;i<20;i++)
	    {
	        printf("%x",AirDate[i]);
	    }
	 /***********************/ 
	 unsigned char DateReceive[20];  
	 void ReceiveDate(unsigned char *date,unsigned char count)
	    {
	        unsigned char i;
	        for(i=0;i<count;i++)
	        {
	            date[i] = AirDate[i];
	        }
	    }
	 /***********************/ 
	    float *fp;
	    ReceiveDate(DateReceive,4);
	    fp = (float *)DateReceive;
	    printf("\nnum=%f",*fp);
	    
	    return 0;
	 }

1. 定义float变量num,存储要传输的数据。

2. 将num的地址强制类型转换为unsigned char指针p。

3. 调用SendDate函数,传入p和字节数4进行传输。

4. SendDate通过p指针拷贝4个字节到AirDate。

5. 在接收端,定义数组DateReceive来接收数据。

6. 调用ReceiveDate并传入DateReceive指针接收数据。

7. 将DateReceive强制转换为float指针fp。

8. 通过解引用fp指针,可以获取转换后的float数据。

9. 打印出转换结果。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值