printf


scanf

指针

&与*的理解

存取变量中的数据有两种引用方式

  1. 直接引用:按变量名称存取变量的值 (int i = 10 使用变量i,这个i即等于10)
  2. 间接引用:将其变量的指针即该变量的内存地址,放在另一个新的变量中,则现在这个新的变量中存储的值就是原来变量的内存地址值,现在对新变量进行操作,就相当于根据原变量内存地址,对原变量进行存取操作!(指正,int &p=10,使用指针变量p等于调用地址,如果要使用它对应的值,需要加上*,下面有介绍)

为了更好理解上述内容,你来看如下知识点

& 取地址
* 指针运算符(取值运算)

关键点:& 和 * 是互为逆运算,即两个在一起相互抵消

  1. #include <stdio.h>
  2. int main(void) {
  3. int p = 1;
  4. int h = *(&p);
  5. printf("%d", h);
  6. return 0;
  7. }
  8. 运行结果 1

指针变量

指针变量就是用来存储地址的变量

任何变量都有它存储的地址,这个地址是随机分配的。如:

int i = 10;

i 这个变量的地址是系统随机分配的。而我们对其进行 “&” 运算之后就可以得到它的地址,并且 (&i) 这个表达式就是一个指向i的指针

指针声明与初始化

格式指针类型 * 指针变量名;

  1. int *number
  2. char *name;

这里介绍两种写法

普通写法

  1. int x;
  2. int *p;
  3. int p = &x;
  4. 前面讲的清楚,指针变量就是用来存储地址的变量,而&是取变量的地址,所以这段代码一下子了然

省略写法

  1. int x;
  2. int *p = &x;


上述两种写法都声明了一个指针p,并且这个指针是指向变量 x 的

对于变量 x, (&x) 则是一个指向x的指针 (指针存放的就是地址, 而&运算得到的地址表达式也就是指向其本身的指针)

并且通过 *(指针) 取到这个地址中的值

错误写法

  1. // 错误1
  2. int *p;
  3. p = 10010; //这里P是指针变量,只可以用来存储地址,而不可以存值
  4. // 错误2
  5. int x = 20;
  6. printf("%d", &(*x) ); //取地址在里面,二者交换一下
  7. // 正确形式:
  8. int i=1, *p, *t;
  9. p = &i;
  10. t = p;
  11. int h = *t;
  12. printf("%d",h);
  13. 输出结果 1

Swap交换两个变量

  1. #include<stdio.h>
  2. void swap(int *x, int *y)
  3. {
  4. int temp;
  5. temp = *x;
  6. *x = *y;
  7. *y = temp;
  8. printf("x=%d, y=%d n", *x, *y);
  9. }
  10. main()
  11. {
  12. int i = 13, j = 45;
  13. swap(&i, &j);
  14. printf("i=%d, j=%dn", i, j);
  15. }

字符数组与指针

字符串 == 字符数组+”结尾

  1. // 对于字符串"Hello world"相当于如下的字符数组
  2. char hi[] = {'h','e','l','l','o',' ','w','o','r','l','d', '\0'};
  3. printf("%c n", hi[1]); // e
  4. printf("%c n", "Hello world"[1]); // e

因为字符串相当于字符数组,所以”Hello world”[1]越数组hi[1]的值一样

数组名 == 起始地址

  1. int a[5] = { 1, 3, 5 };
  2. 1638196
  3. printf("%d", a); // 1638196 #你要是打印啊a[0]的地址值也等于1638196,所以下一行代码成立
  4. printf("%d", *a); // 1
  5. printf("%d", *(a+1)); // 3

运行成功,表示这个变量的类型即使不是指针,也是一个地址

常见用法

  1. int a[5] = { 1, 3, 5 };
  2. int *p;
  3. p = a;
  4. printf("%d", *(p+1)); // 3
  5. printf("%d", *(p+2)); // 5

使用指针遍历

  1. int arr[8] = { 1, 1, 2, 3, 5, 8, 13, 21 };
  2. int i;
  3. int *p;
  4. // 普通遍历
  5. for(i = 0; i < 8; i++)
  6. {
  7. printf("%d ", arr[i]);
  8. }
  9. // 指针遍历
  10. for(p = &(arr[0]); p <= &(arr[7]); p++)
  11. {
  12. printf("%d ", *p);
  13. }
  14. // 数组名获取指针地址
  15. for(p = arr; p < (arr+8); p++)
  16. {
  17. printf("%d ", *p);
  18. }

为了帮助大家去理解指针遍历出———》p = &(arr[0]); p <= &(arr[7]); p++

特意写出如下代码,大家观察

图片.png

指针数组与数组指针

指针数组是数组,数组指针是指针

  1. char *arr[4] = {"hello", "world", "shannxi", "xian"}; #指针数组
  2. //arr就是我定义的一个指针数组,它有四个元素,每个元素是一个char *类型的指针,这些指针存放着其对应字符串的首地址。
  3. char (*pa)[4]; #数组指针

结构体

可以将多种数据类型组合起来的结构

本节课程地址

简介

  1. struct student1
  2. { #包含各种不定量数据类型
  3. int age;
  4. float score;
  5. char sex[2];
  6. };

几种声明方式

常规定义


  1. struct time {
  2. int hour;
  3. int minute;
  4. int second;
  5. };
  6. struct time t;

声明的同时定义


  1. struct time {
  2. int hour;
  3. int minute;
  4. int second;
  5. char sex[2]
  6. } t;

常规定义时定义好结构体后,在外面来定义

而这里是声明的同时就定义

我们一般不推荐这种写法,而是推荐常规写法

使用结构体作为成员


  1. struct DATE{
  2. int year;
  3. int month;
  4. int day;
  5. };
  6. struct person {
  7. char name[256];
  8. struct DATE birthday;
  9. };
  10. struct time t;

匿名结构体

  1. struct {
  2. int hour;
  3. int minute;
  4. int second;
  5. char sex[2]
  6. } t;

你要不细心看,还分不出和声明的同时定义的区别,你看struct后面是不是少了什么

结构体的引用与初始化

如下演示 整体初始化,单个初始化过程

同时也简单展示如何打印信息


#include <stdio.h>

struct student1
{
    int age;
    float score;
    char sex;
};


int main()
{
    struct student1 st={ 80, 66.6, 'F' }; //定义的同时可以整体赋值
    struct student1 st2; //单独定义,自己的小错误,老是忘记写结构体的名字
    st2.age = 10;
    st2.score = 88;
    st2.sex = 'F';

    printf("%d",st.age);

    return 0;


}

结构体数组



#include<stdio.h>
#include<string.h>
struct student
{
    int num;
    char name[20];
    char sex;
    int age;
    float score;
    char addr[30];
};
int main()
{
    int i;
    struct student stu[3]={
        {10101,"LiuHu",'F',18,99.7,"beijing road"},
        {10102,"NaoDan",'M',17,99.8,"shanghai road"},
        {10103,"MaHuangTeng",'F',48,99.9,"shenzhen road"}

    };

    stu[2].age = 12;
    strcpy(stu[2].name,"xiaoyang");        #这个地方为何如此,下面有解释

    for(i=0;i<3;i++)
    {
       printf("%d %s %c %d %1f %s\n",stu[i].num,stu[i].name,stu[i].sex,stu[i].age,stu[i].score,stu[i].addr);
    }
    return 0;
}

解释

C语言只有在定义字符数组的时候才能用“=”来初始化变量,其它情况下是不能直接用“=”来为字符数组赋值的,要为字符数组赋值可以用string.h头文件中的strcpy函数来完成。
例如:
char a[10] = "123"; /*正确,在定义的时候初始化*/
char a[10];
a = "123"; /*错误,不能用“=”直接为字符数组赋值*/
strcpy(a, "123"); /*正确,使用strcpy函数复制字符串*/
所以要对game[0][0].cpart赋值应该用strcpy(game[0][0].cpart, "123");才对。
注意要使用strcpy函数要用#include <string.h>包含string.h头文件。

结构体数组赋值的问题

结构体指针

使用分量运算符"->"来获取成员

结构体指针变量说明的一般形式为: struct 结构名 *结构指针变量名

一个结构体变量的指针就是该结构体变量所占据内存段的起始地址。
可以设一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的起始地址。
同时指针变量也可以用来指向结构体数组中的元素。


结构体指针变量说明的一般形式为:
struct 结构名 *结构指针变量名
例如,在前面的例题中定义了stu这个结构,如果要说明一个指向stu的指针变量pstu,可写为

struct stu *pstu;


当然也可以在定义stu结构时同时说明pstu。与前面讨论的各类指针变量相同,结构体指针变量也必须要先赋值才能使用
赋值就是把结构变量首地址赋予该指针变量,不能把结构名赋予该指针变量。
如果boy是被说明为stu类型的结构变量,则:
pstu = &boy;是正确的
pstu = &stu;是错误的
因为结构名和结构变量是两个不同的概念,不能混淆。结构名只能表示一个结构形式,编译系统并不对他们分配内存空间,只有当某变量被说明为这种类型的结构时,才对该变量存储内存空间。
所以以上 pstu = &stu;是错误的,不可能去取一个结构名的首地址。有了结构指针变量,就更方便地访问结构变量的各个成员。
其访问的一般形式为:
(*结构指针变量).成员名
或为:
结构指针变量->成员名
例如:(*pstu).num
或者:pstu->num
struct stu{
    int num;
    char *name;
    char sex;
    float score;
}boy1 = {006,"zhangzhang",'1',69.6};

int main() {

    struct stu *pstu;
    pstu = &boy1;

    printf("%d,%s\n", boy1.num, boy1.name);

    printf("%d,%s\n", (*pstu).num, (*pstu).name);

    printf("%d,%s\n", pstu->num, pstu->name);

}

image.png

动态内存分配与链表

二者区别

char t[10] = "hi"; 

可变 -----》  内存的动态存储区-----》 

数组在定义的时候在动态内存声明了相应长度的区域,其大小取决于数组的长度,而这个长度是可以修改,也就动态存储
char *p = "hi";

不可变 -----》 内存的静态存储区-----》

“hi"是常量,不能通过"hi" = "en" 来修改,指针p指向的这一块静态内存,其中的数据不能动态更改

动态内存分配

malloc函数—内存动态分配

void * malloc (unsigned int size)

在内存的动态储存区(堆)中分配一个长度为size的连续空间。其参数是一个无符号整数,返回值是一个系统所分配的,连续内存空间的起始地址

若分配内存空间失败,则返回NULL。所以在使用之前一定要判断是否为NULL

PS:该函数仅针对指针使用,使用前需要引用malloc.h库
#include<stdio.h>
#include<malloc.h>

void main()
{

    char *a;
    a = (char *)malloc( 10 * sizeof(char));
    if( a == NULL){
        printf("内存分配失败");
        exit;
    }else{

    *a      = 'H';
    *(a+1) = 'e';
    *(a+2) = 'l';
    *(a+3) = 'l';
    *(a+4) = 'o';
    *(a+5) = '\n';
    *(a+6) = '\0';
    }

    printf(a);

}


运行结果 Hello

free函数—释放动态内存

void * free (void *p)
void main()
{

    char *a,*b;
    a = (char *)malloc(10 * sizeof(char));
    b = a;
    ... #代码省略,大家一看这个函数就简单,只要动态分配都要释放,这个函数放在后面用就行

    free(a);

}

链表

image.png

链表的节点结构

image.png

链表的创建

共同体、枚举、typedef

自定义数据结构

  1. 共同体(union)
  2. 枚举(enum)
  3. 类型定义(typedef)

union 共同体

共同体是多个类型共存,实际只能存储一个类型的变量

至于格式,只需要把结构体关键字struct 换成 union

image.png

enum 枚举

定义某一个数据类型,这种类型只有几种可能,我在定义的同时将这些可能一一例举出来,这样的类型就叫做枚举

#include <stdio.h>
#include <stdlib.h>


enum BOOL{

    FALSE;
    TRUE;

  };

int main()
{

   enum BOOL flag = TRUE;

   if(...)

    ...
}

typedef 类型定义

通过typedef来定义自己的类型

至于为什么这样做,让对方跟容易读懂代码

所以typedef可以理解为把数据类型换个名称不改变性质的这么一个功能

typedef int money


那么这个时候money就是我们定义的类型,实际上跟int没差,但是却能让人更容易读懂你的代码

定义的时候类同

int i = 10;
money m = 200;

printf("int i=%d\nmoney m=%d",i,m);

--------------------------------------------------------------------------------------------------------


#include <stdio.h>
#include <stdlib.h>


typedef int money;

int main()
{
   int i = 10;
   money m = 100;

   printf("int i=%d\nmoney m = %d",i,m);

}

typedef常见用法

#include <stdio.h>
#include <stdlib.h>


typedef struct{

    int hour;
    int minute;

}   TIME;

int main()
{

 原本要
 struct TIME t1;

 现在可以直接

 TIME t1;

}