数组
- int a[10]={0}; 定义整型数组a,里面有10个元素
- char ch[20]={0}; 定义字符数组ch,里面有20个字符
函数
函数的形参和实参的特点:形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的空间。
- 函数命名方法,驼峰命名法:单词首字母大写,例如:void FindMax(){}
- 外部变量、局部变量、全局变量:局部变量为在函数中定义的变量,全局变量为在函数外定义的变量,外部变量(在自定义函数中,可以采用extern 调用全局变量)
main函数
实际上,main函数可以带参数,这个参数可以认为是main函数的形参。C语言规定main函数的参数只能有2个,习惯上这两个参数写为argc和argv。arg(译:参数)
C语言还规定argc(第一个形参)必须是整型,argv(第二个形参)必须是指向字符串的指针数组。
void main(int argc, char* argv[])
static
- 作为变量的存储类型说明符 static int a = 0:定义静态变量,只会在程序第一次执行到该语句时被初始化,只会被执行一次,其作用域仅限于定义该变量的函数内部或者文件内部,不会被其他函数或文件所访问。
- 作为函数的存储类型说明符 static Function():定义静态函数,这种函数只能在定义它的源文件中被调用,其他源文件中不能调用该函数。静态函数可以避免函数名的冲突,同时也可以使得程序的安全性得到提升。(比如,当在两个文件中,定义了同一个函数名,可以使用static进行修饰函数)
- 作为外部变量和函数的作用域限定符:static可以限制外部变量和函数的作用域,使得他们只能在当前文件内部被访问。这样可以避免不同文件之间的变量或函数名的冲突,增强程序的可维护性。
总结:static,可以用于控制变量和函数的生命周期、作用域和访问权限,从而使得程序更加安全、易于维护。static修饰的变量只会被初始化一次,之后再运行到staic int a= 0时,会自动跳过。static修饰的局部变量相当于全局变量。
预处理命令
条件编译
#ifdef 标识符
程序段1
#else
程序段2
#endif
防止头文件被多次引用
#ifndef LED_H
#define LED_H
*
*
#endif
// 或者这样
#pragma once //需要编译器的支持。
指针
**指针(Pointer)其实就是地址,地址就是内存编号。**其一般形式为:*变量名
int* p1; // 表示p1是一个指针变量,它的值是某个整型变量的地址。
char *pc = "C Language"; // 并不是把整个字符串装入指针变量,而是把存放该字符串的字符数组的首地址装入指针变量pc
/*定义一个函数*/
void Function(int *pt){
int a = *pt + 1;
}
// 在调用该函数时,需要传递进去变量的地址,&a
- 数组是由连续的一块内存单元组成,数组名就是这块连续内存单元的首地址。
- 通过指针引用数组元素:
- 下表法:用a[i]形式访问数组元素
- 指针法:采用*(a+i)或*(p+i)形式,用间接访问的方式来访问数组元素。*(p+5)或 *(a+5)就是a[5],因为数组名就是数组元素的首地址,相当于a[0]
char、char*、char a[]、char* a[]
C语言中规定数组代表所在内存位置的首地址.
C语言中没有String这个数据类型,一般用char *表示字符串,或者用char A[]字符数组表示字符串
C语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的终极本质。
-
char *s = "China";
s为一个地址,字符串常量的本质表现是它的第一个字符的地址。 -
char *和char a[]的本质区别
:当定义char a[10]时,编译器会给数组分配10个单元,每个单元的数据类型为字符。而定义char *s时,这是个指针变量,只占4个字节,用来保存一个地址。
总结:char *s只是一个保存字符串首地址的指针变量,char a[]是许多连续的内存单元,每个元素都是char类型 char *s = "hello";等价于char str[] = "hello"
字符串的表示方法
字符串没有单独的类型,所以,它可以用数组或是指针来表示。
用数组表示:
char str[] = {'b','i','t',' '};//第一种表示方法
// 第一种写法不常用,注意加结束符
char str[] = "bit";//第二种表示方法,实际其中存储为 b i t
char *parray[3]; // 存储3个字符串
用指针表示:
char *str2 = "bit";
数组指针和指针数组
前面两个字为修饰词,类型为后面的两个字。优先级:() > [] > *
- (*p)[n]:根据优先级,先看括号内,则p是一个指针,指向一个一维数组,数组长度为n,即数组的指针,数组指针
- *p[n]:根据优先级,先看[],则p是一个数组,再结合 *,这个数组的元素是指针类型,共n个元素,即指针的数组,指针数组
- 1个地址(指针)占4个字节
- 数组指针:数组的指针。例如:int (*p)[3]; // p是一个指针,指向一个数组(数组元素的首地址),数组内是3个整型数据
- 指针数组:指针的数组,是一个数组,内容为指针。例如:int *p[3]; // p是一个数组,里面有3个元素,每个元素都是指针,指向整型数据
#include
int main(int argc, char *argv[])
{
const int max = 3;
static array[] = {1, 2, 3};
for (int i = 0; i max; i++)
{
printf("array[%d] = %dn",i,array[i]);
}
int *p[max];
for (int i = 0; i max; i++)
{
p[i] = &array[i];
printf("array[%d] = %pn",i,&array[i]);
}
return 0;
}
- 指针的指针:char **p; 相当于 char *(*p)
- 变量赋值:char *s = “C Language”; // s是一个指向字符串的指针变量,把字符串的首地址赋予给s
指针变量赋值:
| p = &a | 把变量a的地址赋值给p |
| ————- | ——————————- |
| p = array | 将数组array的首地址传给p |
| p = &array[i] | 将数组array第i个元素的地址赋给p |
指针应用实例
#include
int main(void)
{
int a = 1;
int *p = &a; // 绛変环浜庢崲涓€琛屽啓 p = &a
printf("变量a的地址为: %pn", p);
printf("变量a的值为: %dn", a);
printf("变量a的值为: %dn", *p);
return 0;
}
// ==================指针的运算
static int array[] = {1, 2, 3}; // 这样每次执行时,cpu就不会再重新分配地址了
int i;
int *p = array; // 数组名本身就是一个指针,代表数组元素的首地址
for (i = 0; i 3; i++)
{
printf("array[%d]= %pn", i, p + i);
printf("array[%d]= %dn", i, *(p + i)); // 输出数组的值
}
// 指针的指针
#include
int main(int argc, char *argv[])
{
int a = 1;
int *p1;
int **p2; // 相当于*(*p2)
p1 = &a;
p2 = &p1;
printf("a= %dn", a);
printf("p1 = %pn", p1);
printf("p1 = %dn", *p1); // *表示解引用
printf("p2 = %pn", p2); // p2 = &p1;
printf("**p2 = %dn", **p2); // *表示解引用
return 0;
}
指针传参
// 在调用时,采用&取地址进行变量传递
#include
float average(int *array, int size)
{
float sum = 0;
for (int i = 0; i size; i++)
{
sum += array[i];
}
float avg = sum / size;
return avg;
}
int main(int argc, char *argv[])
{
int student[] = {10,20,30};
float avg = average(student,3); // 数组的名字就是一个指针
printf("平均值:%fn",avg);
return 0;
}
函数返回指针变量
#include
#include
int *GetNumber()
{
static int array[10] = {0}; // C语言不支持调用函数时返回局部变量的地址,所以加一个static
int size = sizeof(array) / sizeof(int); // 数组大小的计算
srand(time(NULL)); // 随机种子
for (int i = 0; i size; i++)
{
array[i] = rand();
printf("%dn", array[i]);
}
return array; // 数组名就是一个指针地址
}
int main()
{
int *p = GetNumber();
for (int i = 0; i 10; i++)
{
printf("*(p+[%d]) =%dn ", i, *(p + i));
}
return 0;
}
结构体、联合体、枚举
结构体(struct)、联合体(union)、枚举(enum)
结构体
- 结构体指针变量的说明和使用:结构指针变量中的值是所指向的结构变量的首地址。 struct 结构名* 结构指针变量名
struct stu *pstu;
- 结构名只能表示一个结构形式,编译系统并不对它分配内存空间,结构指针变量访问成员:
(*pstu).num; // 或pstu->num
为了提高程序的运行效率,最好的办法是使用指针,即用指针变量作为函数参数进行传送,这时由实参传向形参的只是地址,从而减少了时间和空间的开销。
结构体应用实例
示例:结构体赋值
#include
typedef struct
{
int num;
char name[20];
char sex;
int age;
float score;
char address[30];
} Student;
int main()
{
Student stu = {0}; // 结构体实例化
// stu.name = "张三"; 报错,字符串采用下面这种方法进行赋值
strcpy_s(stu.name, "张三"); // _s表示安全类型
stu.age = 1;
stu.sex = 'M'; // 字符类型赋值
return 0;
}
示例:结构体指针的应用(重要)
#include
typedef struct
{
int num;
char name[20];
char sex;
int age;
float score;
char address[30];
} Student;
/*采用指针的方式进行结构体实例化,调用时可以提高程序的运行效率*/
Student *stu;
int main()
{
stu->age = 1;
stu->sex = 'M';
strcpy(stu->name, "张三");
return 0;
}
示例:结构体指针形参
#include
typedef struct
{
int num;
char name[20];
char sex;
int age;
float score;
char address[30];
} Student;
Student stu;
// 如果不用指针,相当于把整个结构体传入过去,使用指针只传递地址就行,大大提升了程序的运行速度
void Print_Student(Student *pt)
{
printf("age = %dn", pt->age);
printf("name = %sn", pt->name);
}
int main()
{
stu.age = 1;
strcpy(stu.name, "张三");
Print_Student(&stu);
return 0;
}
共用体(联合体)
结构体与共用体的区别:
- 结构体:每个成员变量都有自己的内存地址,可以独立地被访问和修改
- 共用体:所有成员变量共享同一块内存空间(共用体的大小等于其最大成员的大小),因此只能同时访问一个成员变量,修改一个成员变量的值将影响其他成员变量的值。
- 结构体的成员变量可以同时存在多个,它们之间没有任何关系;而共用体的成员变量虽然在同一块内存中,但是它们之间的关系是互斥的,每次只有一个成员变量可以被访问
枚举
枚举只是一种基本的数据类型,不是一种构造函数,枚举值是常量,不是变量,可以让数据简洁和已读。注:第一个枚举成员的值为整型0(也可以把第一个元素定义为1),后面的成员值+1。
定义形式
enum 枚举名{枚举值表};
例如:
enum weekday{sun,mou,tue,wed,thu,fri,sat};
enum weekday a,b,c;
或者为:
enum weekday{sun,mou,tue,wed,thu,fri,sat} a,b,c;
或者为:
enum {sun,mou,tue,wed,thu,fri,sat} a,b,c;
#include
enum DAY{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main(){
// 遍历枚举元素
for (day = MON; day SUN; day++) {
printf("枚举元素:%d n", day);
}
}
动态内存分配函数
最常用的内存管理函数是malloc,调用格式:(类型说明符*) malloc(size)
功能:在内存的动态存储区中分配一块长度为 size 字节的联系区域,函数的返回值为该区域的首地址。例如:
pc = (char*)malloc(100);
表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc
释放内存空间函数free
调用形式:free(void *ptr);
功能:释放ptr所指向的一块内存空间,被释放区应是由malloc或calloc函数所分配的区域。
动态内存管理
malloc():memory, allocation,内存分配
位域
位域的分配方式分配空间只能用在结构体和类中。
// 进行位域划分,一个int为4个字节,32bit
// 使用方法,在变量名后加一个:即可
struct Date{
int year:12; //分配12个bit
int month:4; // 分配4个字节
int day:5; // ....
int hour:5;
int minute:6;
int second:6;
};
内存分区与函数类型
由C/C++编译的程序占用的内存分为以下几个部分:
- 栈区(stack):
由编译器自动分配释放
,存放函数的参数值、局部变量的值等 - 堆区(heap):
由程序员分配释放内存
,若程序员不释放,程序结束时可能由OS回收。注:与数据结构中的堆不同,分配方式类似于链表 - 数据区:主要包括静态全局区(static)和常量区
- 代码区:存放函数体的二进制代码
内存操作函数
-
malloc()函数
:memory allocation,动态内存分配,用于申请一块连续的指定大小的内存,new()也是申请动态内存 -
calloc()函数
:malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。 -
free()函数
:释放内存 - memcpy()函数:拷贝内存
- memmove()函数:memove(dest,src,3) // 把src中前3个字符复制给dest
- memset()函数:memset(str,‘A’,4) // 把字符串的前4为设为A,注意要填写ASCII
深拷贝与浅拷贝
- 浅拷贝:多个指针指向同一段内存;直接赋值,例如定义两个字符串,parray[i] = myarray; int a = b;这些都属于浅拷贝,浅拷贝使用比较频繁
- 深拷贝:每个指针指向单独的内存;例如strcpy(dst,str);
假设B复制了A,当修改A时,看B是否会发生变化。如果B也跟着变了,则是浅拷贝,如果B没变,则是深拷贝。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net