黑马程序员-4-C语言学习(复杂数据类型)

---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ----------------------

1. 数组

    a. 简介

        (1) 数组是一种存放数据的集合,它的存储空间跟元素的个数有关

        (2) 数组特点 :

            1) 元素是有序的

            2) 所有元素都是相同类型的

            3) 数组名是数组的首地址,也是第一个元素的首地址

#include <stdio.h>

int main(){
    int arr[3] = {1,2,3};
    printf("数组首地址 : %p ,第一个元素首地址 :%p\n" ,arr ,&(arr[0]));
    // 打印结果 : 数组首地址 : 0028FF30 ,第一个元素首地址 :0028FF30 
    system("pause");
    return 0;    
}

    b. 数组使用

// 初始化方式
	int a[3] = {10, 9, 6};
	int a[3] = {10,9};
	int a[] = {11, 7, 6};
	int a[4] = {[1]=11,[0] = 7};
	// 访问数组元素
	printf("第 %d 个元素 : %d\n" ,0 ,a[0]);

    c. 数组内存结构

        145219_9KpK_2245946.jpg

    d. 数组使用注意

    // 合法初始化方式 
    int arr[3] = {1,2,3};
    int a = 3;
    // 编译会报错
    // 因为有可能变量 a 没被初始化 
    // int arr1[a] = {1,2,3}; 
    // 编译通过但可能有风险
    // 如果 a 没被初始化他的值是一个不确定的值,可能为负数 
    int arr1[a]; 
    // 数组的下标从 0 开始,最后一个元素的下标是 元素个数减一
    a[0] = 0;
    // 最后一个元素
    a[2] = 0;
    // 数组越界,非常危险,因为它可能访问了操作系统的一块重要的内存空间,非常危险
    a[3] = 0;

    d. 二维数组

        (1) 概念 : 二维数组是一个特殊的一维数组:它的元素是一维数组。例如int a[2][3]可以看作由一维数组a[0]和一维数组a[1]组成,这两个一维数组都包含了3个int类型的元素

            145421_H7te_2245946.jpg

        (2) 基本使用

     int a[3][4] = {1,2,3,4,5,6};
     int a[3][4] = {{},{},{}};
     // 数组元素简单访问
     int a[][5] = {3,21,31,2,32,1};
     // 注意错误:
     int a[3][4];
     a[3] = {};


2. 字符串

    a. 简介

        (1) 一个 'p' 是一个字符,简单的字符串 "dog"

        (1) 字符串 : 字符数组,很多个字符组合在一起就是字符串了

        (2) 他具有数组的特点.但字符串通常都有一个隐藏字符作为字符数组的末尾元素 : '\0'

    b. 使用

    // 字符串的初始化
    // 下面等价于 : char a [] = {'1','2','3','\0'};
    char a[] = “123”;
    // 不合格的字符串,只能是字符数组  
    // 打印后面会显示乱码                                                          
    // char a [] = {‘1’,’2’,’3’};
    // 字符串的输出”%s”,’\0’是不会输出的
    printf("%s\n" ,a);    // 123

    c. 内存结构

        151901_9ZFG_2245946.jpg


1. 指针

    a. 简介 - 指针的重要性

        指针是C语言的精华,它是用来存放变量的地址的,而且我们可以通过指针来直接操作变量,因此学好指针对以后C语言的深入研究有很大的帮助。

    b. 指针的简单使用 

    #include <stdio.h>
    int main(){
        int a = 10;
        int *p; // 定义一个指针变量
        p = &a; // 指针变量保存 a 的内存地址
        printf("%d\n",*p); // 通过内存地址访问变量 *代表取出该地址中存储的内容
        
        p = 0;
        p = NULL; // 清空指针操作
        return 0;
    }

    

    c. 指针变量所占用的存储空间

            1) 指针变量用来存储的是变量的地址,那么在64位编译系统中表示一个内存地址用的是64位二进制数,那么存储该二进制数必须要有8byte的内存空间,因此指针变量的大小为8个字节

            2) 指针变量的大小与其类型无关

    #include <stdio.h>
    int main(){
        int a = 10; // int 类型为4个字节
        int *p = &a; 
        
        long b = 10; //long 类型为8个字节
        long *p2 = &b;
        
        printf("int 类型 指针变量的大小为 : %ld\n", sizeof p); // 64位编译器打印结果为8
        
        printf("long 类型 指针变量的大小为 : %ld\n", sizeof p2); // 64位编译器打印结果为8
        return 0;
    }

    d. 为何指针变量要分类型?

            既然知道指针是用来保存变量的内存地址,那么在访问变量的时候是如何取出地址中的内容呢?其实指针变量的类型就告诉指针一次访问要取出该内存地址开始多少个字节

    #include <stdio.h>
    int main(){
        int a = 10; // int 类型为4个字节  a 的地址: 0x7fff5fbff918 , 指针所存储的地址: 0x7fff5fbff918
        long b = 10; //long 类型为8个字节  b 的地址: 0x7fff5fbff910 , 指针所存储的地址: 0x7fff5fbff910
        /*
            按照内存寻址的原则我们可以知道a的地址比b的地址多4个字节 = 8位
        */
        
        int *p = &a; 
        long *p2 = &b;
        
        printf("a 的地址: %p , 指针所存储的地址: %p\n",&a,p);
        
        printf("b 的地址: %p , 指针所存储的地址: %p\n",&b,p2);
        
        printf("int类型指针一次访问所取出内容大小为 : %ld\n", sizeof *p); // int类型指针一次访问所取出内容大小为 : 4
        
        printf("long 类型指针一次访问所取出内容大小为 : %ld\n", sizeof *p2); // long 类型指针一次访问所取出内容大小为 : 8 
        return 0;
    }

    e. 指针和数组

        在数组中规定 : 数组名是指向该数组的首地址,那么数组名就相当于一个指针,因此我们完全可以使用指针来遍历访问数组各个元素。

    #include <stdio.h>
    int main(){
        int array[5] = {1,2,3,4,5};
        int *p = array;
        for (int i =0 ; i < 5 ; i++){
            printf("array[%d] = %d \n",i,*(p+i)); // *(p+i) 先取出对应位置地址再访问
        }
        return 0;
    }
    /* 打印结果 : 
        array[0] = 1 
        array[1] = 2 
        array[2] = 3 
        array[3] = 4 
        array[4] = 5 
    */

    f. 指针与字符串

            字符串本质为字符数组,那么使用指针来访问字符串跟数组相似,不过访问字符串要注意字符串的结束标志和长度防止访问了一块不可用的内存。

    #include <stdio.h>
    #include <string.h>
    int main(){
        char str[] = {'h','e','l','l','o','\0'};   // 等价于 char str[] = "hello";
        int len = strlen(str);
        char *p = str;
        for(int i = 0; i< len ; i++){
            // printf("str[%d] == %c",i,*(p+i));
            printf("str[%d] == %c\n",i,p[i]); // 等价于 printf("str[%d] == %c\n",i,*(p+i));
        }
        return 0;
    }

            使用指针访问字符串常量 : 

    #include <stdio.h>
    #include <string.h>
    /*
        字符串常量与字符串变量区别:
        字符串常量的指针指向的是常量区的内存空间
        字符串变量指向的是栈中分配的内存地址
    */
    int main(){
        //char str[] = {'h','e','l','l','o','\0'};   // 等价于 char str[] = "hello";
        char * str ="hello";
        int len = strlen(str);
        char *p = str;
        int i;
        for(i = 0; i< len ; i++){
            // printf("str[%d] == %c",i,*(p+i));
            printf("str[%d] == %c ,address : %p\n",i,p[i],(p+i));
        }
        system("pause");
        return 0;
    }


    g. 指针和函数指针

        (1) 函数作为一段程序,在内存中也要占据部分存储空间,它也有一个起始地址,即函数的入口地址。函数有自己的地址,那就好办了,我们的指针变量就是用来存储地址的。因此,可以利用一个指针指向一个函数。其中,函数名就代表着函数的地址。

           (2) 指向函数的指针的定义

                    定义的一般形式:函数的返回值类型 (*指针变量名)(形参1, 形参2, ...);

           (3) 指向函数的指针变量主要有两个用途:

                    调用函数

                    将函数作为参数在函数间传递

        #include <stdio.h>
        typedef int (*sum)(int,int);
        void test(sum s,int a,int b){
            int result = s(a,b); //利用函数指针调用函数 打印结果 : 1 + 1 = 2
            printf("%d + %d = %d",a,b,result);
        }
        int add(int a,int b){
            return a+b;
        }
        int main(){
            sum s = add; // 定义一个函数指针
            test(s,1,1);    //把函数指针作为参数传递
            return 0;
        }


 2. 结构体

    a. 结构体简介和简单使用

        (1) 我们知道当多个相同类型的数据整合在一起可以用数组来表示,但如果对于多种不同类型的数据组合成一个整体这时候我们就需要用到C语言提供的结构体来表示。

        (2) 实际开发中,多种类型的数据组合在一起来表示一种新的数据,这个需求非常多。如表示一个学生的信息有 : 姓名 ,年龄 ,身高 ,体重等。

    #include <iostream>
    typedef struct {
        char *name;
        int age;
        double height;
        int weight; 
    } Student;
    
    /*
    如果不用typedef应该这样写
    声明 :
    struct Student{
        char *name;
        int age;
        double height;
        int weight; 
    };
    定义 : 
    struct Student s = {"Mike" ,22 ,175.5 ,50};
    或者 :
    struct {
        char *name;
        int age;
        double height;
        int weight;
    } stu;
    stu.name = "Mike";
    stu.age = 22;
    stu.height = 175.5;
    stu.weight = 50;
    
    */
    
    int main(){
        Student s = {"Mike" ,22 ,175.5 ,50}; 
        printf("name : %s" ,s.name);
        printf("age : %d" ,s.age);
        printf("height : %f" ,s.height);
        printf("weight : %d" ,s.weight);
        return 0;
    }

    b. 结构体使用注意

        (1) 结构体不能递归定义 : 

    // 错误写法
    struct Student {
        char *name;
        struct Student stu;
    };

        (2) 结构体内可以包含其他结构体 : 

    struct Date{
        int year;
        int month;
        int day;
    };
    struct Student {
        char *name;
        struct Date birthday;
    };

        (3) 结构体只有在定义的时候才会分配存储空间,分配存储空间的原则符合对其算法 :

    /*
        对于结构体的对齐算法 :
            在定义变量的时候会依据结构体占用内存最大的成员为单位为结构体变量分配存储空间
    */
    #include <stdio.h>
    // 声明结构体,不会分配存储空间
    typedef struct {
        char *name; // 64位编译器 : 占用内存8byte
        int age; // 4byte
    } Student;
    
    int main (){
        Student s = {"Mike" ,22}; //定义结构体变量会分配存储空间
        printf("size is : %ld\n",sizeof s); // size is : 8 ,符合对齐算法
        return 0;
    }

        (4) 结构体初始化问题 :

    #include <stdio.h>
    typedef struct {
        char *name; // 64位编译器 : 占用内存8byte
        int age; // 4byte
    } Student;
    
    int main(){
        Student s = {"Mike" ,22};    // 对于这种初始化方式{..}只适用声明变量的时候
        // s = {"Mike" ,22}; // 错误写法
        return 0;
    }

        (5) 相同类型的结构体可以进行赋值:

     #include <stdio.h>
    typedef struct {
        char *name; // 64位编译器 : 占用内存8byte
        int age; // 4byte
    } Student;
    
    int main(){
        Student s = {"Mike" ,22};    // 对于这种初始化方式{..}只适用声明变量的时候
        Student s2 = s;
        printf("name is : %s ,age is : %d\n",s2.name,s2.age); // name is : Mike ,age is : 22
        return 0;
    }

        (6) 结构体指针适用注意 :

   /*
        结构体指针变量的定义形式:struct 结构体名称 *指针变量名
        有了指向结构体的指针,那么就有3种访问结构体成员的方式
        结构体变量名.成员名
        (*指针变量名).成员名
        指针变量名->成员名 
    */
    #include <stdio.h>
     typedef struct {
        char *name; // 64位编译器 : 占用内存8byte
        int age; // 4byte
    } Student;
    
    int main(){   
        Student *s;
        Student s1 = {"Mike" ,22};
        s = &s1;

        printf("name is : %s ,age is : %d\n",s->name,s->age); // name is : Mike ,age is : 22
        printf("name is : %s ,age is : %d\n",(*s).name,(*s).age); // name is : Mike ,age is : 22
        system("pause");
        return 0;
    }


3. 枚举

    a. 简介

         枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数。当一个变量有几个固定的可能取值时,可以将这个变量定义为枚举类型。比如,你可以用一个枚举类型的变量来表示季节,因为季节只有4种可能的取值:春天、夏天、秋天、冬天。 

    b. 使用

    //  一般形式为:enum 枚举名 {枚举元素1,枚举元素2,……}; 
    // 下面方式均等价
    // (1)
    // 定义枚举类型
    enum WeedDay {Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday};
    // 定义枚举变量
    enum WeedDay day;
    // (2)
    enum WeedDay {Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday} day;
    // (3)
    enum {Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday} day;

    c. 使用注意

    // (1) C语言编译器会将枚举元素{Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday}作为整型常量处,因此叫枚举常量
    // (2) 枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。
    //   默认        0       1        2        3        4    5        6    
    enum WeedDay {Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday};
    
    // (3) 也可以在定义枚举类型时改变枚举元素的值,如果定义了某个值后面的值会依次递增
    //               0       1        2        4          5      6        7    
    enum WeedDay {Monday,Tuesday,Wednesday,Thursday = 4,Friday,Saturary,Sunday};
    
    // (4) 枚举遍历
    // 定义枚举变量
    enum {Monday,Tuesday,Wednesday,Thursday,Friday,Saturary,Sunday} day;
    // 给变量赋值
    day = Monday;
    // 遍历枚举元素
    for (day = Tuesday; day <= Sunday; day++){
        printf("枚举元素 : %d \n",day);
    }










---------------------- Java培训.Net培训Android培训IOS培训、期待与您交流! ---------------------- 

 详情请查看:http://edu.csdn.net/heima



转载于:https://my.oschina.net/u/2245946/blog/323225

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值