C语言查漏补缺笔记

1.数组指针、指针数组、数组指针数组
2.指针函数、函数指针
3.结构体数组、结构体指针、结构体数组指针
4.函数传参:指针变量传参、数组传参、结构体传参
5.内存四区:代码区、静态区、堆区、栈区
6.位操作
7.预处理器宏定义
8.数据声明
9.修饰符的使用
10.访问固定内存位置

0.函数名:是一个地址
数组:数组名是一个常量符号,一定不要放到=号的左边
指针:在32位系统中,指针就4个字节,跟指针类型无关
char *p1;
int *p2;
float a=1.2;
int *p=&a;
printf(“p=%x\n”, *p);

1.数组指针、指针数组、数组指针数组

数组指针:int (*p)[4];
存储的是数据类型为( int[4] )的数组的地址,是一个存储数组地址的指针变量,sizeof§为4个字节

指针数组:int *a[5]
相当于int *(a[5])
他是数组,是一个元素类型为指针的数组:int *()[5]
sizeof(a)为20个字节

数组指针数组:int (a[2])[3]
他是一个数组,他的元素类型是数组指针:int (
[2])[3]

2.指针函数、函数指针
指针函数:返回值类型为指针的都是指针函数,和它的参数类型没关系。
int *add()

函数指针:存储函数地址的指针变量,跟他的返回值和参数类型都没关系。
int (*p)(int a, int b);//定义了一个函数指针,指向的是返回值类型是整型,2个整型参数。
int add(int a, int b);
p=add;
add(10,20);相当于p(10,20);或者(*p)(10,20)

3.结构体数组、结构体指针、结构体数组指针

结构体数组
struct stu{
}class[5];

结构体指针
struct stu{
char *name;
}stu1={};

struct stu *pstu = & stu1;
(*pstu).name 或 pstu->name

结构体数组指针
struct stu{
}stus[]={
},*ps;

for(ps=stus;ps<stus+len;ps++){
printf("%s", ps->name);
}

int len = sizeof(stus)/sizeof(struct stu);

4.函数传参:指针变量传参、数组传参、结构体传参

指针变量传参
int add(int *a, int *b) { return a+b }
int a=10,b=20;
num=add(&a, &b);

数组传参
传递数组首元素地址
int add( int *num );
int add( int num[20] );
int add( int num[] );

结构体传参
https://blog.csdn.net/lin37985/article/details/38582027/

#include <iostream>
using namespace std;
void ImageCenterCalculation(struct size *ScreenSize, struct size *ImageSize);

struct size
{
 int width;
 int high;
};

int main()
{
 cout << "Hello World" << endl; // 输出 Hello World
 struct size ScreenSize, ImageSize;
 ImageCenterCalculation(&ScreenSize,&ImageSize);
 printf("%d\n%d\n%d\n%d\n", ScreenSize.high, ScreenSize.width, ImageSize.high, ImageSize.width);
 system("pause");
 return 0;
}

void ImageCenterCalculation(struct size *ScreenSize, struct size *ImageSize)
{
 ScreenSize->high = 1;
 ScreenSize->width = 1;
 ImageSize->high = 2;
 ImageSize->width = 2;
}

5.内存四区:代码区、静态区、堆区、栈区
代码区
代码区code,程序被操作系统加载到内存的时候,所有的可执行代码都加载到代码区,也叫代码段,这块内存是不可以在运行期间修改的。

静态区
所有的全局变量以及程序中的静态变量都存储到静态区。

栈区
栈stack是一种先进后出的内存结构,所有的局部变量(自动变量),函数的形参都是由编译器自动放出栈中,当一个自动变量超出其作用域时,自动从栈中弹出。

堆区:动态分配
堆heap和栈一样,也是一种在程序运行过程中可以随时修改的内存区域,但没有栈那样先进后出的顺序。
堆是一个大容器,它的容量要远远大于栈,但是在C语言中,堆内存空间的申请和释放需要手动通过代码来完成。
int *p = (int *)malloc( sizeof(int) *10 ); // 堆数组
memset(p,0,sizeof(int)*10 );
free§;

int array[10] = {0} ;//栈数组

6.位操作
给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
最佳的解决方案如下:

#define BIT3 (0x1 << 3)
static int a;

void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}

7.预处理器宏定义
A . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

  1. #define 语法的基本知识:不能以分号结束,括号的使用,等等
    2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
  2. 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
  3. UL(表示无符号长整型)。

B . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
(1) 在 嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
(2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码。
(3)懂得在宏中小心地把参数用括号括起来
(4)宏的副作用,例如:当你写下面的代码时会发生什么事?
least = MIN(*p++, b);

8.数据声明
用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
d)一个有10个整型数的数组( An array of 10 integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )

答案是:
a) int a; // An integer
b) int *a; // A pointer to an integer
c) int **a; // A pointer to a pointer to an integer
d) int a[10]; // An array of 10 integers
e) int *a[10]; // An array of 10 pointers to integers
f) int (*a)[10]; // A pointer to an array of 10 integers
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer

9.修饰符的使用

关键字static的作用
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

关键字const

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

前两个的作用是一样,a是一个常整型数。
第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。
1)关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

关键字volatile
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1)并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量

1)一个参数既可以是const还可以是volatile吗?解释为什么。
2); 一个指针可以是volatile 吗?解释为什么。
3); 下面的函数有什么错误:

int square(volatile int *ptr)
{
return *ptr * *ptr;
}

下面是答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3) 这段代码有点变态。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

10.访问固定内存位置
典型的类似代码如下:
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值