C语言 —— 指针和字符型数组

在介绍字符型数组与指针的关系前,先回忆一下字符型数组:

字符数组是C语言中存储字符串的基本方式,它的特点如下:

  • 在内存中连续存储;

  • 以'\0'作为字符串结束标志;

  • 数组名代表数组首地址。

字符指针

字符指针可以指向字符串常量

cs 复制代码
char *p = "Hello";

字符串常量存放在字符串常量区,当指针指向字符串常量时,其内容不可以被更改。

这是因为指针变量本身存储在栈区或者静态区,其指向的的字符串存储在只读数据段,也就是字符串常量区。

关键字:const

const用于定义常量,表示被修饰的内容不能被修改。

其作用:保护数据不被意外修改,提高代码可读性。

const与指针结合时较为复杂,比如:

指向常量的指针(指针可变,指向的内容不可变)

cs 复制代码
const int *p ;
int a = 10;
p = &a;
//*prt = 20;          //错误:不能通过p修改a的值
a = 20;               //直接修改a是可以的

常量指针(指针不可变,指向的内容可变)

cs 复制代码
int b = 20;
int *const p = &b;
*p = 40;
//p = &a;     // 不能改变p的指向

const与数组

cs 复制代码
const int a = {1,5,6,2,8,9};
a[0] = 2;  //不能修改const的数组元素

(注:const变量必须初始化)

void *

其表示万能指针,但不能进行指针运算。

将一个字符型数组中的值复制给另一个字符型数组时,了解到字符型数组的复制需要调用strcpy函数,要是复制整形数组,我们只需要调用memcpy函数,其内核与strlen大相径庭,如下

cs 复制代码
#include <stdio.h>

void Memcpy(void *dest,void *src,int n)
{
    char *p = (char *)dest;
    char *q = (char *)src;
    while(n--)
    {
        *p++ = *q++;
    }
}

int main(void)
{
    int a[10] = {1,2,3,4,5,6,7,8,9,0};
    int b[10];
    Memcpy(b,a,sizeof(a));
    for(int i = 0,i < 10;++i)
    {
        printf("%d\n",b[i]);       //运行后,就能清楚的看到将数组a复制到了数组b
    }
    return 0;
}

(memcpy函数时一个字节一个字节的复制,所以传的参数就是数组的字节长度)

万能指针的基类型时 void *,它降低了程序额耦合性。

数组指针

数组指针 int (*p)[10]:指向数组的指针,基类型为int [10](即长度为10的一维整形数组)

sizeof(p)= 40

(注:其中的括号不能省略,就变成了指针数组)

*(*(a + i) + j) * 等价于 a[ i ][ j ]

*a 表示第一行元素的地址,所以*(a+i)表示第 i 行元素的首元素地址;

*(a + i) + j :表示第 i 行第 j 列元素的地址;

*(*(a + i ) + j):表示第 i 行 第 j 列的值。

水平逆序二维数组

cs 复制代码
void swap(int *a,int *b)
{
    int t = *a,
    *a = *b;
    *b = t;
}
void reverse(int *begin,int *end)
{
    while(begin < end)
    {
        swap(begin++ , end--);
    }
}

void reverse2D(int (*a)[4],int rows)
{
    int i,j;
    for(i = 0;i < rows; ++i)
    {
        reverse(*(a + i),*(a + i) + 3);
    }
}

二维数组作为函数传参,传递形参指向一维数组的指针。

指针函数

指针函数指的是返回值为指针的函数,它不能返回局部变量地址,但能返回静态变量,或者称其为全局变量。(static 关键字能修改变量的生存期)。

例子:标准函数库中的strcpy和strcat

cs 复制代码
char *Strcpy(char *dest,const char *src)
{
    char *ret = dest;
    while(*dest)
    {
        ++dest;
    }
    while(*src)
    {
        *dest++ = *src++;
    }
    *dest = 0;
    return ret;
}

char  *Strcat(char *dest,const char *src)
{
    char *ret = *dest;

    while(*dest)
    {
        ++dest;
    }
    while(*src)
    {
        *dest++ = *src++;
    }
    *dest = 0;
    return ret;
}

字符数组复制、连接、比较

(1)复制strcpy 与 strncpy

cs 复制代码
void Strcpy(char *dest,char *src)
{
    while(*src)
    {
        *dest++ = *src++;
    }
    *dest = '\0';
}

void Strncpy(char *dest,const char *src,int n)
{
    while(*src && n--)
    {
        *dest++ = *src++;
    }
    *dest = 0;
}

strcpy是复制全部元素

strncpy是复制前 n 个元素

(2)strcat 与 strncat

cs 复制代码
void Strcat(char *dest,const char *src)
{
    while(*dest)
    {
        ++dest;
    }
    while(*src)
    {
        *dest++ = *src++;
    }
    *dest = 0;
}

void Strncat(char *dest,const char *src,int n)
{
    while(*dest)
    {
        dest++;
    }
    while(*src && n--)
    {
        *dest++ = *src++;
    }
    *dest = 0;
}

stccat是将数组src全部元素除了 '\0' 全部拷贝到数组dest

strncat是将数组src 前 n 个字符拷贝到数组dest

(3)strcmp 与 strncmp

cs 复制代码
int Strcmp(const char *s1,const char *s2)
{
    while(*s1 == *s2 && *s1 && *s2)
    {
        ++s1;
        ++s2;
    }
    return *s1 - *s2;
}

int Strncmp(const char *s1,const char *s2,int n)
{
    while(--n && *s1 == *s2 && *s1 && *s2)
    {
        ++s1;
        ++s2;
    }
    return *s1 - *s2;
}

同理,两者的区别也是后者比较 s1 和 s2 前n个元素的大小