C语言快速学习笔记之一

适合读者

有一门或多门语言基础的人学习,适合快速入门。所以假设读者已经了解基本的 赋值循环遍历定义变量定义数组

基础数据类型

基本类型、枚举类型、void、派生类型。这里先说基本类型,其他类型随后再讲。

基本类型

数据类型 长度 名称
char 1 bytes 字节类型
short 2 bytes 短整型
int 4 bytes 整形
float 4 bytes 单精度浮点型
long 4 bytes 长整型
double 8 bytes 双精度浮点

以上类型还可以使用 unsigned 来进行修饰,如此就变成了

数据类型 长度 名称
char 1 bytes 字节类型
unsigned char 1 bytes 无符号字节类型
short 2 bytes 短整型
unsigned short 2 bytes 无符号短整型
int 4 bytes 整形
unsigned int 4 bytes 无符号整形
float 4 bytes 单精度浮点型
unsigned float 4 bytes 无符号单精度浮点型
long 4 bytes 长整型
unsigned long 4 bytes 无符号长整型
double 8 bytes 双精度浮点
unsigned double 8 bytes 无符号双精度浮点
long double 16 bytes 长双精度浮点

使用 unsigned 修饰的类型 是无符号类型,也就是都是正数,没有负数。

注意这里没有字符串类型,没有列表类型。需要指针配合数组一起实现。
以上内容了解个大概即可。使用的过程中不懂的可以再查。

重要概念

使用几个列子说明:

eg:数组

// 定义一个长度为100的数组,并将数组内的元素初始化为0。
int arr_int1[100] = {0};
//使用 1-6定义一个int类型的数组。
int arr_int2[] ={1,2,3,4,5,6};
//定义一个数组长度为20,类型为int
int arr_int3[20];
//为数组赋值。
for (int i = 0; i < 20; i++)
{
arr_int3[i] = i;
}

eg:指针 1

//定义一个整型变量
int a = 10;
//定义一个整型变量的指针,地址指向 变量a的地址。"&" 是取地址操作。
int* p = &a;
// “” 对于 指针而言是取值操作,p指向的是地址,p就是取这个地址中的值,即为 10.
int b = *p;
// 这里 ab, 所以会输出 equal.
if (a
b){
printf("equal.");
}

eg:指针和字符串 2

/*
定义一个char类型的指针,将 “this is a string.” 赋值给指针变量。

字符串再内存中的存储方式是数组形式,这个实际上做的操作是,将字符串中首字母t的内存地址存放到了指针变量 str 中。
/
char str = "this is a string.";
//计算 字符串 str的长度。
char len = strlen(str);
//上面的例子讲到 * 是取值操作,所以这里 直接拿到指针变量str的值,并当做char输出,其结果是 t 。
printf("
str = %c\n",
str);
//将str指针变量的地址 输出。这里注意,str本身就是指针变量,这里再次对其进行取地址,也就是取str的存放地址。
printf("&str = %p\n",&str);
//这里直接输出str的内容,以%p的方式输出,也就是以内存地址方式表示,实际上是字符串t的地址。
printf("str = %p\n",str);
//这里跟上一句唯一的区别就是%s ,得到的结果是 this is a string.
printf("str = %s\n",str);
for (int i = 0; i < len; i++)
{
/*
&(str[i]):str[i]是字符串的内容,使用&取地址,就是字符串每个字节对应的内存地址。
(&str[i]):str[i]是内容,则 &str[i]是每个元素的地址,(&str[i])就是地址内对应的值,也就是每个元素了。上文中提到过 “*”是取值操作。
/
printf("str[%d] = %p\t %c\n",i,&(str[i]),
(&str[i]));
}

最终结果如下:

*str = t
&str = 000000000061FE10
str = 000000000040407C
str = this is a string.
str[0] = 000000000040407C t
str[1] = 000000000040407D h
str[2] = 000000000040407E i
str[3] = 000000000040407F s
str[4] = 0000000000404080
str[5] = 0000000000404081 i
str[6] = 0000000000404082 s
str[7] = 0000000000404083
str[8] = 0000000000404084 a
str[9] = 0000000000404085
str[10] = 0000000000404086 s
str[11] = 0000000000404087 t
str[12] = 0000000000404088 r
str[13] = 0000000000404089 i
str[14] = 000000000040408A n
str[15] = 000000000040408B g
str[16] = 000000000040408C .

最后得到结论:

字符串在内存中的存放地址是连续的

将字符串直接赋值给指针遍历,则是将字符串元素的首地址赋值给指针变量。

eg:指针和数组 3

char char_str[] = "this is a string.";
printf("char_str = %c\n",char_str);
printf("&char_str = %p\n",&char_str);
printf("char_str = %p\n",char_str);
for (int i = 0; i < strlen(char_str); i++)
{
printf("char_str[%d] = %p \t char is : %c\n",i,&(char_str[i]),char_str[i]);
}

最后运行结果如下:

*char_str = t
&char_str = 000000000061FDF0
char_str = 000000000061FDF0
char_str[0] = 000000000061FDF0 char is : t
char_str[1] = 000000000061FDF1 char is : h
char_str[2] = 000000000061FDF2 char is : i
char_str[3] = 000000000061FDF3 char is : s
char_str[4] = 000000000061FDF4 char is :
char_str[5] = 000000000061FDF5 char is : i
char_str[6] = 000000000061FDF6 char is : s
char_str[7] = 000000000061FDF7 char is :
char_str[8] = 000000000061FDF8 char is : a
char_str[9] = 000000000061FDF9 char is :
char_str[10] = 000000000061FDFA char is : s
char_str[11] = 000000000061FDFB char is : t
char_str[12] = 000000000061FDFC char is : r
char_str[13] = 000000000061FDFD char is : i
char_str[14] = 000000000061FDFE char is : n
char_str[15] = 000000000061FDFF char is : g
char_str[16] = 000000000061FE00 char is : .

这个例子和上面一个例子区别不算太大,但有一个细微的差别就是:

char数组变量的内存地址 == 字符串元素的首地址。

char指针变量的内存地址 != 字符串元素的首地址。

这个强调一个数组长度的问题,直接说结论了:

char * str = "12234234";
//数组长度
int arr_len = sizeof(str)/sizeof(str[0]);

如果这个数组是从别的方法中传递过来的,可能这样的方式就不对了。因为当一个指针从外部传入内部函数的时候,这时指针的类型会丢失。正确的做法是将数组长度当作参数一起传递。

eg:指针参数 4

看这么一个方法:

int indexOf(char *str, char chr)
{
char *p = strchr(str, chr);
if (p == 0)
{
return -1;
}
else
{
for (int i = 0; i < strlen(str); i++)
{
if (p == &str[i])
{
return i;
}
}
}
return -1;
}

strchr方法返回 chr 字符 在 str字符串中的位置的地址,
如果 str = "ABC",chr = 'A' 那么 返回的是 A这个位置所在的内存地址。我们通过指针的对比,实现了 indexOf方法。如果存在查找的字符串,返回第一次出现的位置索引,否则返回-1.

eg:指向函数的指针 4

可以做回调函数使用

int max(int x, int y)
{
return x > y ? x : y;
}

int getGt(int x, int y, int (*func)(int, int))
{

int i = func(x, y);
printf("Max number is %d\n", i);
return i;
}

//call
getGt(1,2,&max);

上述例子写了一个函数max去对比大小。写了一个getGT方法可以用来操作2个整型数据,第三个参数是一个指向函数的指针 int (*func)(int, int),他传入了2个参数,并返回了int类型数据。
那么这个getGT方法的功能就可以完全依赖这个func的功能,比如还可以这样操作。

int add(int x, int y)
{
return x + y;
}

int getGt(int x, int y, int (*func)(int, int))
{

int i = func(x, y);
printf("Max number is %d\n", i);
return i;
}

//call
getGt(1,2,&add);

那么此时这个方法就是求和运算。

eg:空类型指针 5

void*可以接受任何类型的指针地址。可以用作接受可变类型的参数使用。类似于C#的泛型。

void * p ;
int * p_int;
int a = 12;
p = &a;
p_int = p;
printf("%d",*p_int);

由上面的例子不难看出,p_int 的地址是由 p传递过去的,p的地址是变量a的地址。所以对p_int取值的时候结果是12.

eg:拥有可变参数的函数

前提:需要引入 #include <stdarg.h>

int getSuperGt(int (*func)(int, int), int x, ...)
{

va_list valist;
va_start(valist, x);
int c = 0, i = 0;
while (i < x)
{
c = func(c, va_arg(valist, int));
i++;
};
va_end(valist);
printf("max number is %d\n", c);
return c;
}

参数列表第一个参数是 指向函数的指针。
第二个参数是 int x,指定可变参数的个数。
第三个参数 ... 是三个点组成。代表后续的整型参数。

va_list valist; //定义一个va_list变量,接受参数。

va_start(valist,x); //调用va_start方法指定参数个数有x个。

va_arg(valist,int); // 使用 va_arg来访问参数,类似迭代器。 并指定参数的类型是 int类型。

最后调用 va_end(valist);结束可变参数的使用。

结合上面的例子,这个方法的使用方式是这样的:

//结果返回5
getSuperGt(&max,5,1,2,3,4,5);

枚举

1、定义

//定义枚举类型Day
enum Day{
Mon;
Tue;
Wen;
Thu;
Fri;
Sat;
Sun;
};

//定义枚举类型Day 并定义Day的变量 day
enum Day{
Mon;
Tue;
Wen;
Thu;
Fri;
Sat;
Sun;
} day;

//定义枚举类型Day 并定义Day的变量 day ,第一个元素值是1,第二个是2,以此类推。跟java和C#的枚举很像。
enum Day{
Mon = 1;
Tue;
Wen;
Thu;
Fri;
Sat;
Sun;
} day;

//定义一个变量,并赋值第一个元素 Mon
enum Day workday = Mon;

//将 2 赋值给 workday ,做了类型转换。
workday = (enum Day)2;

未完待续...

posted @ 2022-09-14 15:10  莫问哥哥  阅读(144)  评论(0)    收藏  举报