文档章节

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

 明天过后1
发布于 2014/09/29 15:46
字数 3087
阅读 46
收藏 2

---------------------- 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. 数组内存结构

        

    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类型的元素

            

        (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. 内存结构

        


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



© 著作权归作者所有

共有 人打赏支持
粉丝 2
博文 23
码字总数 36166
作品 0
佛山
C语言编程新手基础学习:Windows编程入门

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界
05/22
0
0
C语言再学习--关键字

C语言一共有32个关键字,如下表所示: 关键字 说明 auto 声明自动变量 short 声明短整型变量或函数 int 声明整型变量或函数 long 声明长整型变量或函数 float 声明浮点型变量或函数 double 声...

qq_29350001
2016/11/03
0
0
编程技巧之嵌入式C语言完全学习笔记及案例[图]

编程技巧之嵌入式C语言完全学习笔记及案例[图] 1、经典入门:hello world 1.1、概念: *源文件:源文件即源代码文件,C语言源文件后缀名是.c。 *头文件:头文件后缀名为.h(head,头),C语言...

原创小博客
08/15
0
0
学C语言不知道从哪儿下手?学习框架都帮你列好了!

随着高考结束,在未来计算机专业讲又要迎来一批新生小鲜肉!C语言入门到入土,从长发飘飘到秃顶的老铁也越来越多,其中,想自学成才的也占了不少数;想笨鸟先飞的也占有不少数。 C语言上手可...

这个人很懒什么都没留下
06/29
0
0
一个资深C语言工程师说如何学习C语言

谈及C语言,我想C语言功能强大都应该知道、应用广泛,一旦掌握了后,你就可以理直气壮地对他人说“我是电脑高手!”,而且以后若是再自学其他语言就显得轻而易举了。忧虑的是,C语言般博大精...

小辰GG
2017/12/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java序列化(四) - 实现Externalnalizable接口

实现Externalnalizable接口 实现Externalnalizable接口 package meng.springboot.demo.obj;import java.io.Externalizable;import java.io.IOException;import java.io.ObjectInput......

晨猫
25分钟前
0
0
php 日志库获取调用方的代码文件地址和代码行数

在使用其他语言的打印日志的时候,经常能看到打印日志时带上文件地址和代码行数,对于调试和查找问题非常方便,但是 php 日志库里则很少见到这个功能,但这个功能还是可以实现的。 关键点就是...

anoty
31分钟前
6
0
Android Studio如何批量导入全部包import

当需要导包时,Android Studio有单个导包快捷键 Alt+Enter 但是没有全部的包 但是可以在设置里设置Auto Import自动导入功能

lanyu96
33分钟前
0
0
六款优秀的 Linux 基准测试工具

基准测试是指运行计算机程序去评估硬件和软件性能的行为。硬件基本测试包括评估处理器,内存,显卡,硬盘,网络等不同组件的性能。基准测试有两类: 复合和应用。复合基准对一个硬件执行压力...

openthings
35分钟前
2
0
什么是阿里云容器服务?

关于阿里云容器服务的详细内容:阿里云容器服务使用教程 容器服务(Container Service)提供高性能可伸缩的容器应用管理服务,支持用 Docker 容器进行应用生命周期管理,提供多种应用发布方式...

mcy0425
36分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部