[简明C语言]C语言概述P_4:define定义常量和宏/指针/结构体
前言
C语言功能强大,使用灵活,既可以用于编写应用软件,又用于编写系统软件。熟练掌握C语言是在计算机科学与技术专业学习中计算机开发人员的一项基本功。本文记录了简明C语言_C语言概述第四部分内容。
1. 初识常量和宏
#define定义常量和宏
define是一个预处理指令:
1.denfine定义常量:
#include <stdio.h>
#define MAX 1000
int main()
{
printf("%d\n", MAX);//1000
return 0;
}
先用#define把MAX定义为1000,这里MAX是一个标识符常量(本质上是一个常量),所有使用MAX的地方都会被自动替换为1000。
这样做的好处是如果这个常量在程序中多次使用,那么修改起来非常方便,只需要把#define那行代码修改即可。
比方说如果要把程序中所有的MAX改成100,只需要把#define MAX 1000改成#define MAX 100即可。
一般习惯把标识符常量所有字母大写。
2. define定义宏:
先来看一段define定义的宏代码:
#include <stdio.h>
#define ADD(X,Y) X+Y//简单写成X+Y
int main()
{
printf("%d\n", ADD(2, 3));//5
return 0;
}
运行结果为:
可以发现此时运行结果X+Y=5并没有什么问题。
但是如果想要对宏ADD进行一个乘4的操作:
#include <stdio.h>
#define ADD(X,Y) X+Y//简单写成X+Y
int main()
{
printf("%d\n", 4*ADD(2, 3));//4*ADD
return 0;
}
那么运行的结果将会是:
可以发现,此时运行出来的结果并不是期望中的4 * 5=20,而是被运行成为了4 * 2 + 3 = 11
这是因为,宏替换只是简单地用定义的宏体去替换宏名而不进行任何计算。
打印中的ADD(2,3)被直接替换成了2 + 3,打印的内容也变成了4 * 2 + 3 = 11
就是说宏定义的直接替换,就是最直接的替换!
因此,宏定义中若出现表达式时,圆括号的有无,最终效果明显不同。为了保证定义在置换后仍保持正确的运算顺序,经常在定义中使用必要的圆括号将字符串括起来。
想要实现预期的效果,就要对宏的定义做出一些调整:
#include <stdio.h>
#define ADD(X,Y) ((X)+(Y))
//X可能不是一个变量,而是一个表达式
//Y可能不是一个变量,而是一个表达式
//宏体
int main()
{
printf("%d\n", 4*ADD(2, 3));
return 0;
}
调整后的运行结果:
可以发现此时结果为4 * (2 + 3) = 20,结果达到预期效果。
2. 初识指针
指针也就是内存地址,指针变量是用来存放内存地址的变量。
那么在初识指针之前,先了解一下内存
内存
内存是计算机上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效地访问到内存的每个单元,就给单元进行了编号,这些编号被称为该内存单元的地址。
[为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编号是唯一的,根据编号可以准确地找到每个字节]
思考两个问题:
- 内存是怎么编号的?
- 一个这样的单元是多大空间?
内存的编号
现代电脑操作系统可以分为32位和64位,如32位处理器中就有32根地址线(物理线),当接通电源后,每条线都会有电信号,产生正1、0信号(正电为1,负电为0)。
也就是说,把电信号转换成数字信息——1和0组成的二进制序列。
32位产生的二进制序列:
这些序列都可以作为内存的编号,也称为内存单元的地址。
64位同理。
内存单元的空间大小
一个存储单元可以存储一个字节,也就是8个二进制位。计算机的存储器容量是以字节为最小单位来计算的,对于一个有128个存储单元的存储器,可以说它的容量为128字节。
即,一个单元是一个字节,然后分配地址
地址
看下面一段代码:
int main()
{
int a = 10;//a在内存中要分配空间 - 4个字节
return 0;
}
按F10逐过程调试到int a = 10;,打开内存窗口:
在内存窗口中输入&a
,即取地址a:
另外,使用监视窗口监视&a
,同样也可以看到a的地址:
让每行显示4个字节来观察:
首先需要了解16进制的一些基本概念:
0x是16进制的前缀;
16进制中10是A,11是B,以此类推到15是F。
比如说0a是2个16进制数,可以转换成8个2进制数,也就是一个字节。
那么可以看到对于这4个字节来说:
0x000000D1030FF994就是0a的地址;
0x000000D1030FF995就是后面第一个00的地址;
0x000000D1030FF996就是后面第二个00的地址;
0x000000D1030FF997就是后面第三个00的地址;
那么a占了四个字节,就是94-97这四个地址。
当一个变量占四个字节时,取地址取到的则是四个字节中第一个字节的地址。
打印地址看看:
#include <stdio.h>
int main()
{
int a = 10;//a在内存中要分配空间 - 4个字节
printf("%p\n", &a);//%p 专门用来打印地址的
return 0;
}
运行结果:
为什么每次打印出的地址不一样?
程序每次执行时都会让系统分配需要的内存(RAM),而程序结束后系统会把它们删除,下次会重新分配。
局部变量是自动创建,自动销毁的,在执行结束后销毁,在下一次重新创建时随机分配的内存地址又是新的。
地址有一定几率相同。
指针
数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。
指针变量
指针变量的定义方式:
#include <stdio.h>
int main()
{
int a = 10;//a在内存中要分配空间 - 4个字节
printf("%p\n", &a);//%p 专门用来打印地址的
//指针变量的定义方式
int* pa = &a;//pa是用来存放地址的,在C语言中pa叫做指针变量
//* 说明 pa是指针变量
//int 说明pa指向的对象是int类型的
char ch = 'w';
char* pc = &ch;
return 0;
}
在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。
解引用
解引用操作符*
- 它可用于访问或操作存储在由指针指向的内存位置的数据。
- 任何应用于解引用指针的操作都会直接影响它指向的变量的值。
也就是说:解引用就是通过指针,找到对应的内存和内存中的数据。我们可以通过解引用访问或修改指针指向的内存内容。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;//* - 解引用操作
//*pa就是以pa的内容为地址的变量a
//*pa就是用过pa中的地址找到a
printf("%d\n", a);
return 0;
}
运行结果:
可以看到变量a的值被修改成了20。
解引用图示:
int num = 10;——即在内存中占用四个字节存放10,当取出num的地址时,其实是取出num四个字节中第一个字节的地址(最小的)也就是44,再将这个地址存到指针变量p中,所以p中存放的地址指向num。
使用*p可以将变量num的值修改。
以整型指针举例,可以推广到其他类型,如:
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'q';
printf("%c\n", ch);
return 0;
}
运行结果:
指针变量的大小
指针变量占用的内存空间大小根据所使用的操作系统及编译环境而定,指针类型占用的字节数是不同的。 一般而言:16位机器的代码,指针占2个字节。 32位机器的代码,指针占4个字节。 64位机器的代码,指针占8个字节。
打印指针变量的大小:
#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%lld\n", sizeof(char*));
printf("%lld\n", sizeof(short*));
printf("%lld\n", sizeof(int*));
printf("%lld\n", sizeof(long*));
printf("%lld\n", sizeof(long long*));
printf("%lld\n", sizeof(float*));
printf("%lld\n", sizeof(double*));
return 0;
}
运行结果:
由于这里使用的是64位平台,指针占8个字节。
如果使用32位平台,这里打印出的结果应该全都是4。
指针的大小为什么是相同的呢?
指针是用来存放地址的!
指针需要多大空间,取决于地址的存储需要多大空间。
32位机器上需要32个比特位,即4个字节;
64位机器上需要64个比特位,即8个字节。
3. 初识结构体
结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含:名字+年龄+性别+学号这几项信息。
这里只能使用结构体来描述了。
#include <stdio.h>
//结构体可以让C语言创建新的类型
//创建一个学生类型
struct Stu
{
char name[20];//成员变量
int age;
double score;
};
//创建一个书的类型
struct Book
{
char name[20];
float price;
char id[30];
};
int main()
{
struct Stu s = { "张三", 20, 85.5 };//结构体的创建和初始化
printf("1:%s %d %lf\n", s.name, s.age, s.score);//结构体变量.成员变量
//.操作符 - 访问结构体的成员变量
struct Stu* ps = &s;//结构体的地址
printf("2:%s %d %lf\n", (*ps).name, (*ps).age, (*ps).score);//结构体变量.成员变量
//2和3等价
printf("3:%s %d %lf\n", ps->name, ps->age, ps->score);//指针ps指向->对象的成员
// 结构体指针 -> 成员变量
return 0;
}
运行结果:
总结
本文初步介绍了define定义常量和宏、指针、结构体帮助认识C语言。
到此为止第一章:C语言概述部分就已经全部分享完了。
相信朋友们对C语言的内容也应该有了一个大致的了解。