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;
// 这里 a==b, 所以会输出 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;
未完待续...