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;

未完待续...

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