C语言复习第六天

指针的本质分析

*号的意义

  1. 在指针声明时,*号表示所声明的变量为指针
  2. 在指针使用时,*号表示取指针所指向的内存空间的值

tips
int i=0;
int j=0;

int *p=&i; 变量p保存着变量i的内存地址,即: p<–>&i *p<---->i
j=*p;

使用示例:

#include <stdio.h>

int main()
{
    int i = 0;
    int* pI;
    char* pC;
    float* pF;    
    pI = &i;  
    *pI = 10;    
    printf("%p, %p, %d\n", pI, &i, i);
    printf("%d, %d, %p\n", sizeof(int*), sizeof(pI), &pI);
    printf("%d, %d, %p\n", sizeof(char*), sizeof(pC), &pC);
    printf("%d, %d, %p\n", sizeof(float*), sizeof(pF), &pF);    
    return 0;
}

tips
64位系统里指针变量都是8个字节,因为地址是用8个字节去表示。
在这里插入图片描述

传值调用与传址调用

  1. 指针是变量,因此可以声明指针参数
  2. 当一个函数体内部需要改变实参的值,则需要使用指针参数
  3. 函数调用时实参值将复制到形参
  4. 指针适用于复杂数据类型作为参数的函数中

使用示例(交换两个变量的值):

#include <stdio.h>
int swap(int* a, int* b)
{
    int c = *a;    
    *a = *b;    
    *b = c;
}
int main()
{
    int aa = 1;
    int bb = 2;   
    printf("aa = %d, bb = %d\n", aa, bb);    
    swap(&aa, &bb);    
    printf("aa = %d, bb = %d\n", aa, bb);    
    return 0;
}

常量与指针

const int *p;           //p可以改变,p指向的内容不能修改
int const* p;           //p可以改变,p指向的内容不能修改
int *const p;           //p不可以改变,p指向的内容可以修改
const int* const p;     //都不可以改变

//口诀:  (*号)左数右指(不能修改,为常量)
示例:
```c
#include <stdio.h>

int main()
{
    int i = 0;
    
    const int* p1 = &i;
    int const* p2 = &i;
    int* const p3 = &i;
    const int* const p4 = &i;
    
    *p1 = 1;    // compile error
    p1 = NULL;  // ok
    
    *p2 = 2;    // compile error
    p2 = NULL;  // ok
    
    *p3 = 3;    // ok
    p3 = NULL;  // compile error
    
    *p4 = 4;    // compile error
    p4 = NULL;  // compile error
    
    return 0;
}

在这里插入图片描述

数组的本质分析

  1. 数组是相同类型变量的有序集合
    在这里插入图片描述

数组的大小

  1. 数组在一片连续的内存空间中存储元素
  2. 数组元素的个数可以显示或隐式指定。
int a[5]={1,2};
int b[l]={1,2};

编程示例:

#include <stdio.h>

int main()
{
    int a[5] = {1, 2};
    int b[] = {1, 2};
    
    printf("a[2] = %d\n", a[2]);
    printf("a[3] = %d\n", a[3]);
    printf("a[4] = %d\n", a[4]);
    
    printf("sizeof(a) = %d\n", sizeof(a));
    printf("sizeof(b) = %d\n", sizeof(b));
    printf("count for a: %d\n", sizeof(a)/sizeof(int));
    printf("count for b: %d\n", sizeof(b)/sizeof(int));
    
    return 0;
}

数组地址与数组名

  1. 数组名代表数组首元素的地址
  2. 数组的地址需要用取地址符&才能得到
  3. 数组首元素的地址值与数组的地址值相同
  4. 数组的首元素的地址与数组的地址是两个不同的概念
#include <stdio.h>
int main()
{
    int a[5] = { 0 };

    printf("a = %p\n", a);
    printf("&a = %p\n", &a);
    printf("&a[0] = %p\n", &a[0]);
    return 0;
}

数组名的盲点

  1. 数组名可以看做一个常量指针
  2. 数组名“指向”的是内存中数组的首元素的起始位置
  3. 数组名不包含数组的长度信息。
  4. 在表达式中数组名只能作为右值使用
  5. 只有在下列场合中数组名不能看做常量指针
    ——数组名作为sizeof操作符的参数
    ——数组名作为&运算符的参数

示例:

#include <stdio.h>

int main()
{
    int a[5] = {0};
    int b[2];
    int* p = NULL;
    
    p = a;
    
    printf("a = %p\n", a);
    printf("&a = %p\n",&a);
    printf("p = %p\n", p);
    printf("&p = %p\n", &p);
    printf("sizeof(a) = %d\n", sizeof(a));
    printf("sizeof(p) = %d\n", sizeof(p));
   
    printf("\n");

    p = b;
    
    printf("b = %p\n", b);
    printf("p = %p\n", p);
    printf("&p = %p\n", &p);
    printf("sizeof(b) = %d\n", sizeof(b));
    printf("sizeof(p) = %d\n", sizeof(p));
    
    //b = a;
  
    return 0;
}

在这里插入图片描述
tips
可以发现,数组名的大小是数组的大小,而指针的大小就是这个指针变量的大小(64位系统占8个字节)

数组小结

  1. 数组是一片连续的内存空间
  2. 数组的地址和数组首元素的地址意义不同
  3. 数组名在大多数情况下被当成常量指针处理
  4. 数组名起始并不是指针,不能将其等同于指针。

指针和数组的分析(上)

数组的本质

  1. 数组是一段连续的空间
  2. 数组的空间大小为sizeof(array_type)*array_size
  3. 数组名可以看做指向数组第一个元素的常量指针

指针的运算

指针是一种特殊的变量,与整数的运算规则为:
	p+n;<-->(unsigned int)p+n*sizeof(*p)

结论
当指针p指向一个同类型的数组的元素时:p+1将指向当前元素的下一个元素;p-1将指向当前元素的上一个元素。

指针的运算

  1. 指针之间只支持减法运算
  2. 参与剑法运算的指针类型必须相同

p1-p2;<——>((unsigned int)p1-(unsigend int)p2)/sizoef(type)

tips:
只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差
当两个指针指向的元素不在同一个数组中时,结果未定义。
示例:

#include <stdio.h>

int main()
{
    char s1[] = {'H', 'e', 'l', 'l', 'o'};
    int i = 0;
    char s2[] = {'W', 'o', 'r', 'l', 'd'};
    char* p0 = s1;
    char* p1 = &s1[3];
    char* p2 = s2;
    int* p = &i;
	
    printf("%d\n", p0 - p1);
    printf("%d\n", p0 + p2);
    printf("%d\n", p0 - p2);
    printf("%d\n", p0 - p);
    printf("%d\n", p0 * p2);
    printf("%d\n", p0 / p2);
	
    return 0;
}

在这里插入图片描述

警告的两句的运行结果:在这里插入图片描述

指针的比较

  1. 指针也可以进行关系运算
  2. 指针关系运算的前提是同时指向同一个数组中的元素
  3. 任意两个指针之间的比较运算(==,!=)无限制
  4. 参与比较运算的指针类型必须相同
#include <stdio.h>

#define DIM(a) (sizeof(a) / sizeof(*a))

int main()
{
    char s[] = {'H', 'e', 'l', 'l', 'o','\0'};
    char *s1="hello";
    char* pBegin = s;
    char* pEnd = s + DIM(s); // Key point
    char* p = NULL;
    
    printf("pBegin = %p\n", pBegin);
    printf("pEnd = %p\n", pEnd);
    printf("s=%s\n",s);
    printf("Size: %d\n", pEnd - pBegin);
	
    for(p=pBegin; p<pEnd; p++)
    {
        printf("%c\n", *p);
    }
    
    printf("\n");
   
    return 0;
}

在这里插入图片描述

小结

  1. 数组声明时编译器自动分配一片连续的内存空间
  2. 指针声明时只分配了用于容纳地址值的4或8字节空间(看是32位还是64位系统)
  3. 指针和整数可以进行运算,其结果为指针
  4. 指针之间只支持减法运算,其结果为数组元素下标差
  5. 指针之间支持比较运算,其类型必须相同

指针和数组分析(下)

数组的访问方式

在这里插入图片描述
效率比较:

  1. 指针以固定的增量在数组中移动时,效率高于下标形式
  2. 指针增量为1且硬件具有硬件增量模型时,效率更高
  3. 下标形式于指针形式的相互转换:
    在这里插入图片描述
    tips:
    下标形式的可读性高,指针形式效率稍高
    示例:
#include <stdio.h>

int main()
{
    int a[5] = {0};
    int* p = a;
    int i = 0;
    
    for(i=0; i<5; i++)
    {
        p[i] = i + 1;
    }
    
    for(i=0; i<5; i++)
    {
        printf("a[%d] = %d\n", i, *(a + i));
    }
    
    printf("\n");
    
    for(i=0; i<5; i++)
    {
        i[a] = i + 10;
    }
    
    for(i=0; i<5; i++)
    {
        printf("p[%d] = %d\n", i, p[i]);
    }
    
    return 0;
}

在这里插入图片描述
指针和数组不同:

#include <stdio.h>

int main()
{
    extern int a[];   //定义的是数组
    
    printf("&a = %p\n", &a);
    printf("sizeof(&a) = %d\n", sizeof(&a));
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);

    
    return 0;
}

在这里插入图片描述

而如果改为指针类型

#include <stdio.h>

int main()
{
    extern int* a;
    
    printf("&a = %p\n", &a);
    printf("sizeof(&a) = %d\n", sizeof(&a));
    printf("a = %p\n", a);
    printf("*a = %d\n", *a);

    
    return 0;
}

则:运行结果有误
在这里插入图片描述

a和&a的区别

  1. a为数组的首元素的地址
  2. &a为整个数组的地址
  3. a和&a的区别在于指针运算
    在这里插入图片描述
#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, \n", p1[-1]);
    //printf("%d, %\n", p2[0]);
    printf("%d, \n", p3[1]);
    return 0;
}

在这里插入图片描述

数组参数

数组作为函数参数时,编译器将其编译成对应的指针

void f(int a[]);<——>void f(int* a);
void f(int a[5]);<——>void f(int* a);

tips:
一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

示例:

#include <stdio.h>

void func1(char a[5])
{
    printf("In func1: sizeof(a) = %d\n", sizeof(a));
    
    *a = 'a';
    
    a = NULL;
}

void func2(char b[])
{
    printf("In func2: sizeof(b) = %d\n", sizeof(b));
    
    *b = 'b';
    
    b = NULL;
}

int main()
{
    char array[10] = {0};
    
    func1(array);
    
    printf("array[0] = %c\n", array[0]);
    
    func2(array);
    
    printf("array[0] = %c\n", array[0]);
    
    return 0;
}

在这里插入图片描述

小结

  1. 数组名和指针仅使用方式相同
    ——数组名的本质不是指针
    ——指针的本质不是数组
  2. 数组名并不是数组的地址,而是数组首元素的地址
  3. 函数的数组参数退化为指针

C语言中的字符串

字符串的概念

  1. 字符串是有序字符的集合
  2. 字符串是程序中的基本元素之一
  3. C语言中没有字符串的概念
    ——C语言通过特殊的字符数组模拟字符串
    ——C语言的字符串是以==’\0’==结尾的字符数组

字符数组与字符串

  1. 在C语言中,双引号引用的单个或多个字符是一种的特殊的字面量
    ——存储于程序的全局只读存储区
    ——本质为字符数组,编译器自动在结尾加上’\0’
    代码示例:
#include <stdio.h>

int main()
{
    char ca[] = {'H', 'e', 'l', 'l', 'o'};
    char sa[] = {'W', 'o', 'r', 'l', 'd', '\0'};
    char ss[] = "Hello world!";
    char* str = "Hello world!";
    
    printf("%s\n", ca);
    printf("%s\n", sa);
    printf("%s\n", ss);
    printf("%s\n", str);
    
    return 0;
}

运行结果:
在这里插入图片描述
tips
字符串的打印是以’\0’为结果的。

字符串的小秘密

  1. 字符串字面量的本质是一个数组
  2. 字符串的字面量可以看作常量指针
  3. 字符串字面量中的字符不可改变
  4. 字符串字面量至少包含一个字符

字符串字面量

“hello world!”是一个无名的字符数组。

使用示例:

#include <stdio.h>
int main()
{
    char b = "abc"[0];
    char c = *("123" + 1);
    char t = *"";
    
    printf("%c\n", b);
    printf("%c\n", c);
    printf("%d\n", t);
    
    printf("%s\n", "Hello");
    printf("%p\n", "World");  
    return 0;
}

在这里插入图片描述

字符串的长度

  1. 字符串的长度就是字符串所包含的字符的个数
  2. 字符串长度指的是第一个‘\0’字符前出现的字符个数
  3. 通过’\0’结束符来确定字符串的长度
  4. 函数strlen用于返回字符串的长度。
#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "Hello\0world";
    int i = 0; 
    for(i=0; i<sizeof(s)/sizeof(char); i++)
    {
        printf("%c\n", s[i]);
    } 
    printf("%s\n", s);
    printf( "%d\n", strlen(s) );
    printf( "%d\n", strlen("123") );
    return 0;
}

自己的递归实现strlen函数

int  my_strlen(char *str)
{
  static int length =0;
  if(str==NULL)
    return 0;
  else if(*str!='\0')
  {
    length++;
    my_strlen(str+1);   
  }
   return length;
}
int main()
{
    char s[] = "Hello\0world";
   
    printf( "%d\n", my_strlen(s) );
    
    return 0;
}

在这里插入图片描述

字符串小结

  1. C语言中通过字符数组模拟字符串
  2. C语言中的字符串使用==’\0’作为结束符==
  3. 字符串字面量的本质为字符数组
  4. 字符串相关函数都依赖于结束符’\0’
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值