文章目录
数据类型
数 据 类 型 { 简 单 类 型 { 基 本 类 型 { 整 型 ( i n t ) 浮 点 型 ( f l o a t , d o u b l e ) 字 符 型 ( c h a r ) 空 类 型 ( v o i d ) 用 户 基 本 类 型 枚 举 类 型 ( e n u m ) 结 构 类 型 { 数 组 ( [ ] ) 结 构 ( s t r u c t ) 联 合 ( u n i o n ) 类 ( c l a s s ) 指 针 类 型 ( ∗ ) 数据类型 \begin{cases} 简单类型 \begin{cases} 基本类型 \begin{cases} 整型 (int) \\浮点型(float,double) \\字符型(char) \\空类型 (void) \end{cases}\\ \\用户基本类型 \quad 枚举类型(enum) \end{cases}\\ \\结构类型 \begin{cases} 数组([\ ]) \\结构(struct) \\联合(union) \\类(class) \end{cases}\\ \\指针类型 (*) \end{cases}\\ 数据类型⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧简单类型⎩⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎧基本类型⎩⎪⎪⎪⎨⎪⎪⎪⎧整型(int)浮点型(float,double)字符型(char)空类型(void)用户基本类型枚举类型(enum)结构类型⎩⎪⎪⎪⎨⎪⎪⎪⎧数组([ ])结构(struct)联合(union)类(class)指针类型(∗)
数据类型本质:固定内存大小的别名。
c++通过引用可以对同一块内存起多个别名
函数是一种特殊的数据类型.
数组类型
c语言3大难点
- 定义一个数组类型
- 定义一个数组指针
- 数组类型和数组指针关系
数组做函数参数会退化为指针,正确做法是传递数组内存首地址和数组有效长度。这是c的特色之一。
数组元素个数:
n = sizeof(a) / sizeof(a[0])
//或者事先宏定义:
#define DIM(a) (sizeof(a) / sizeof(*a))
n = DIM(a);
//但函数中的多维数组要注意指针退化问题,n会变为4/4.
int a[]
a
代表首元素地址;
&a
代表整个数组的地址。
不存在void
类型的变量,因为编译器不知道如何分配内存。
void*
可以指向任何类型的地址。
- 作为左值用于接收任何类型的指针
- 作为右值赋值给其它指针时需要强制类型转换。
赋值
直接复制和间接赋值。
间接赋值举例:*(int *)0x1234 = 1
,相当于直接修改内存。
变量三要素
名称,大小,作用域。
内存四区
代码区,全局区,堆,栈。
栈
由编译器自动分配和释放,存储函数得参数,局部变量等。
函数返回局部变量(临时变量)的地址时会出现warning或者宕机,release
和debug
模式结果不一样。
main函数调用func
函数:
| func()参数 |
|----------------|
| func()返回地址 |
|----------------|
| main()运行状态 |
|----------------| <-----执行func()
| main()参数 |
|----------------|
| main()返回地址 |
|----------------|
| 操作系统运行状态 |
|-----------------|
堆
由程序员分配和释放。
全局区
存储全局变量和静态变量,程序结束后由os释放。
代码区
存储二进制代码。
指针
c语言两大江山:
- 多级指针
- 函数指针
指针也是种数据类型,与指向的内存空间的数据类型相关,指针步长由该类型决定。
一级指针可参考c字符串部分
p + p
没有意义;
p - p
有意义.
野指针
/*
注意野指针问题
释放空间,但没有重置指针。
避免方法:指针初始化时和释放后及时置为NULL。
*/
if(p != NULL)
{
free(p);
//p = NULL;
}
间接赋值
利用指针进行间接赋值,是指针的意义所在,是C的最大特色。
3个条件:
- 定义指针
p
和要指向的数据data
p = &data
*p
间接修改data
输入输出特性
in: 主调函数分配内存传给被调函数。
out:被调函数分配内存传给主调函数。
const
const char *p;
,修饰内存空间;
char* const p
,修饰指针。
malloc()
搭配malloc()
使用时,切记要free()
。
其实,malloc()
在实际项目中不能轻易使用。
真正应该使用该函数的情况是,在函数中分配的空间在函数结束后还需要存在。
指针参数
函数有指针参数,切记先判断指针是否为NULL。!!!!!!!!!!!!!!!!!!
C字符串
- 以
'\0'
结尾 - 用字符数组模拟
- 可分配在栈上、堆上和全局区
初始化
1. 指定长度
char s[100] = {'a','b','c','d'};
其余自动置为0
2.不指定长度
char s[] = {'a','b','c','d'};
编译器自动求元素个数;
不以0结尾;
3.用字符串初始化数组
char s[] = "abc";
以0结尾
作为数组,长度为4,即sizeof()
作为字符串,长度为3,即strlen()
指针操作字符串
中括号[]
到星号*
的推导过程:
char s[];
s[i]
–>s[0+i]
–>*(s+i)
区别:s
是只读的常量地址,而指针可变。
/*
字符串copy
*/
//假设dest和src都开辟了内存
while( (*dest = *src) != '\0')
{
src++;
dest++;
}
while( (*dest++ = *src++) != '\0')
{}
while( (*dest++ = *src++) );
一级指针内存模型
char s[] = "aaa";
"aaa"
位于全局区和栈区。
char *p1 = "bbb";
全局区。
char *p2 = (char*)malloc(10);
strcpy(p2,"ccc");
"ccc"
位于全局区和堆区。
二级指针
第一种模型
全局区
#include<stdio.h>
#include<string.h>
void print(char **a,int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
printf("%s\n",*(a+i));
}
}
void sort(char **a,int n)
{
int i,j;
char *tmp = NULL;
for(i = 0; i < n; i++)
{
for(j = i + 1; j < n; j++)
{
if(strcmp(a[i],a[j]) > 0)
{
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}
int main(int argc, char *argv[])
{
char *a[] = {
"bbb","ccc","aaa"
};
int n = sizeof(a) / sizeof(a[0]); // 12/4
print(a,n);
sort(a,n);
printf(" after sort():\n");
print(a,n);
return 0;
}
第二种模型
栈区
#include<stdio.h>
#include<string.h>
void print(char a[5][10],int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
printf("%s\n",*(a+i));
}
}
void sort(char a[5][10],int n)
{
int i,j;
char tmp[10] = {0};
for(i = 0; i < n; i++)
{
for(j = i + 1; j < n; j++)
{
if(strcmp(a[i],a[j]) > 0)
{
strcpy(tmp,a[i]);
strcpy(a[i],a[j]);
strcpy(a[j],tmp);
}
}
}
}
int main(int argc, char *argv[])
{
char a[5][10] = {
"bbb","ccc","aaa"
};
int n = sizeof(a) / sizeof(a[0]); // row : 50/10 != 3
printf("%s",a[4]);
print(a,n);
sort(a,n);
printf(" after sort():\n");
print(a,n);
return 0;
}
第三种模型
堆区.
这种模型一定要写得健壮,及时释放内存。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int getMem(char ***s,int n);
void print(char **a,int n);
void sort_point(char **a,int n);
void sort_mem(char **a,int n);
void freeMem(char ***buf,int n);
int getMem(char ***s,int n)
{
char **a = NULL;
int i;
if(s == NULL)
{
perror("s can't be NULL.");
return -1;
}
a = (char**)malloc(n * sizeof(char*));
if(a == NULL)
{
perror("malloc() error.");
return -1;
}
memset(a,0,n * sizeof(char*));
for(i = 0; i < n; i++)
{
a[i] = (char*)malloc(10);
if(a[i] == NULL)
{
perror("malloc() error.");
freeMem(&a,n);
return -1;
}
snprintf(a[i],10,"%c%c%c",i+65,i+65,i+65);
}
*s = a;
return 0;
}
void print(char **a,int n)
{
int i = 0;
for(i = 0; i < n; i++)
{
printf("%s\n",*(a+i));
}
}
void sort_point(char **a,int n)
{
int i,j;
char *tmp = NULL;
for(i = 0; i < n; i++)
{
for(j = i + 1; j < n; j++)
{
if(strcmp(a[i],a[j]) < 0)
{
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}
void sort_mem(char **a,int n)
{
int i,j;
char tmp[10] = {0};
for(i = 0; i < n; i++)
{
for(j = i + 1; j < n; j++)
{
if(strcmp(a[i],a[j]) > 0)
{
strcpy(tmp,a[i]);
strcpy(a[i],a[j]);
strcpy(a[j],tmp);
}
}
}
}
void freeMem(char ***buf,int n)
{
char **p = NULL;
int i;
if(buf == NULL)
{
return ;
}
p = *buf;
if(p == NULL)
{
return ;
}
else{
for(i = 0; i < n; i++)
{
if(p[i] != NULL)
{
free(p[i]);
p[i] = NULL;
}
}
}
}
int main(int argc, char *argv[])
{
char **a = NULL;
int n = 3;
int i;
if(getMem(&a,n) != 0)
{
return -1;
}
print(a,n);
printf(" after sort_point():\n");
sort_point(a,n);
print(a,n);
printf(" after sort_mem():\n");
sort_mem(a,n);
print(a,n);
return 0;
}