《C程序设计语言》学习笔记(05)指针与数组

第5章 指针与数组

 
指针:保存变量地址的变量

5.1 指针与地址

  • 一个字节 - 存char
  • 两个字节 - 存 short
  • 四个字节 - 存 long,int

地址运算符 &:去一个内存对象的地址。

间接寻址(引用)运算符 *:作用于指针时,将访问指针所指向的对象。

// 声明:
double *p, atof(char *); 
// *p 指向 double 类型的指针
// 指向 char 类型的指针
  1. 每个指针都必须指向某种特定的数据类型

  2. 指针改变对象的值

  3. 指针直接使用

    int *p, x=1;
    p = &x;  // 1.指针p 指向 x
    *p = 0;  // 2.指针p 改变对象 x 的值
    int *q;
    q = p;  // 3.指针q也指向p 所指向的对象
    

     

5.2 指针与函数参数【将输入的下一个整型数赋值给 *pn】

指针参数使被调函数能够访问和修改主调函数中对象的值。(址传递)

swap(&a, &b)

void swap(int *px, int *py)
{
    int temp;
    temp = *px;
    *px = *py;
    *py = temp;
}
/* getint(int *) 将输入的下一个整型数赋值给 *pn */
#include <stdio.h>
#include <ctype.h>

int getch(void); //读下一个字符
void ungetch(int); //字符放回输入中

// 将输入的下一个整型数赋值给 *pn
int getint(int *pn)
{
    int c, sign;
    while(isspace(c = getch()))
        ;
    if(!isdigit(c) && c != EOF && c != '+' && c != '-')
    {
        ungetch(c);         // 输入不是数字时,返回0
        return 0;
    }
    sign = (c == '-')?-1 : 1; // sign 存正负号
    if(c == '+' || c == '-') //若有正负号,继续读下一个
        c = getch();
    for(*pn = 0; isdigit(c); c = getch()) // 是数字,则给到 *pn
        *pn = 10 * *pn + (c - '0');
    *pn *= sign;             // 最后加上正负
    if(c != EOF)
        ungetch();           // 若最后读入不是 EOF,则写回
    return c;
}

 

5.3 指针与数组

指针比数组下标编写的程序执行速度快。

数组名代表数组最开始的一个元素地址。不是变量,不可以进行数值运算。a=pa 和 a++ 非法

int *pa, a[10];
pa = a; //指针pa指向 a[0]
// 由于数组名代表数组第一个元素的地址,等价 pa=&a[0];

a[i] -> *(a+i) -> *(pa+i) -> pa[i] //等价

函数定义时,func(char s[])func(char *s) 等价。
 

5.4 地址算数运算【字符串长度】

有效的指针运算包括:

  1. 相同类型指针之间赋值运算;

  2. 指针同整数之间的加减运算;

    p+n 表示p指向的对象之后第n个对象的地址,如果int类型占4个字节,则n按4的倍数计算。

  3. 指向相同数组元素的指针间减法或比较运算;

  4. 将指针赋值为0或指针与0之间比较运算。

其他形式的指针运算都非法,例如

  • 指针间加法、乘法、除法、移位或屏蔽运算;
  • 指针同 float 或 double 类型之间的加法运算;
  • 不经强制类型转换,不同类型指针之间赋值运算。(void * 类型除外)
/* 返回字符串长度:strlen */
int strlen(char *s)
{
    char *p = s;
    while(p != '\0')
        p++;
    return  p-s;
}

 

5.5 字符指针与函数【字符串复制比较】

char amsg[] = "now is the time"; //定义一维数组

  • amsg 是仅足以存放字符串以及’\0’的一维数组;
  • 单个字符可修改,如:amsg[2]=‘b’;
  • amsg 始终指向同一存储位置。

char pmsg[] ="now is the time"; //定义指向字符串常量的指针

  • 不能改字符串内容;
  • 可修改指向其他地址。
/* 字符串复制 */
// 版本1:数组下标
void strcpy(char *s, char *t)
{
    int i;
    i = 0;
    while((s[i] = t[i]) != '\0')
        i++;
}

// 版本2:指针实现
void strcpy(char *s, char *t)
{
    while((*s++ = *t++) != '\0')
        ;
}

// 版本3:优化
void strcpy(char *s, char *t)
{
    while(*s++ = *t++) // 表达式同'\0'的比较是多余的,只需判断表达式值是否为0即可
        ;
}

/* 字符串比较 */
// 版本1:数组下标
int strcmp(char *s, char *t)
{
    int i;
    for(i=0; s[i] == t[i]; ++i)
        if(s[i] == '\0') //默认s,t 长度相等
            return 0;
    return s[i] - t[i];
}

// 版本2:指针方式
int strcmp(char *s, char *t)
{
    for(; *s == *t; s++, t++)
        if(*s == '\0')
            return 0;
    return *s - *t;
}

*p++ = val; //入栈

val = *–p; //出栈
 

5.6 指针数组以及指向指针的指针【文本排序、快速排序】

  • 读取所有输入行;
  • 对文本行进行排序;
  • 按次序打印文本行。

指针数组的声明:char *lineptr[MAXLINES];

/* 对输入的文本进行排序:readlines, writelines, qsort, getline, *alloc, afree */
#include <stdio.h>
#include <string.h>

#define MAXLINES 5000 //最大文本行

char *lineptr[MAXLINES]; //指向文本行的指针数组

int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);

void qsort(char *lineptr[], int left, int right);

int main()
{
    int nlines; //读取输入行数目
    if((nlines = readlines(lineptr, MAXLINES)) >= 0) // 读取输入行数,如果大于0
    {
        qsort(lineptr, 1, nlines-1); //排序
        writelines(lineptr, nlines); //输出
        return 0;
    }
    else
    {
        printf("error: input too big to sort\n");
        return 1;
    }
    return 0;
}

#define MAXLEN 1000
int getLine(char *, int);
char *alloc(int);

/* 读取输入行 */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];
    nlines = 0;
    while((len = getLine(line, MAXLEN)) > 0)
    {
        if(nlines >= maxlines || (p = alloc(len)) == NULL)
            return -1;
        else
        {
            line[len-1] = '\0';
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    }
    return nlines;
}

/* 将行保存到 s 中,并返回该行的长度*/
int getLine(char s[], int lim)
{
    int c, i;
    i = 0;
    while(--lim > 0 && (c=getchar()) != EOF && c != '\n')
       s[i++] = c;
    if(c == '\n')
        s[i++] = '\n';
    s[i] = '\0';
    return i;
}

#define ALLOCSIZE 1000
static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;

char *alloc(int n)
{
    if(allocbuf + ALLOCSIZE - allocp >= n)
    {
        allocp += n;
        return allocp - n;
    }
    else
        return 0;
}
void afree(char *p)
{
    if(p >= allocbuf && p < allocbuf + ALLOCSIZE)
        allocp = p;
}

/* 写输出行 */
void writelines(char *lineptr[], int nlines)
{
    int i;
    for(i=0; i<nlines; i++)
        printf("%s\n", lineptr[i]);
}


// 版本2:文本行排序,数组元素是字符串,v[left]... v[right]
void qsort(char *v[], int left, int right)
{
    int i, last;
    void swap(char *v[], int i, int j);
    if(left >= right)       // 左右下标碰到,排序结束
        return;
    swap(v, left, (left+right)/2);  // 把 mid 拿到最前面,作为比较的参照物
    last = left;                    // last 移动到v[0]
    for(i = left+1; i<=right; ++i) // 遍历从 left+1 到 right
        if(strcmp(v[i], v[left]) < 0)
            // 小于v[left]的元素交换到第二个元素位置,并往后推移
            swap(v, ++last, i);
    swap(v, left, last);            // 交换left, last,如此v[last]固定位置
    qsort(v, left, last-1);
    qsort(v, last+1, right);
}

void swap(char *v[], int i, int j)
{
    char *temp;
    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}


 
快速排序:qsort (版本2)文本行

// 版本2:文本行排序,数组元素是字符串,v[left]... v[right]
void qsort(char *v[], int left, int right)
{
    int i, last;
    void swap(int *v[], int i, int j);
    if(left >= right)       // 左右下标碰到,排序结束
        return;
    swap(v, left, (left+right)/2);  // 把 mid 拿到最前面,作为比较的参照物
    last = left;                    // last 移动到v[0]
    for(i = left+1; i<=right; ++i) // 遍历从 left+1 到 right
        if(strcmp(v[i], v[left]) < 0)
            // 小于v[left]的元素交换到第二个元素位置,并往后推移
            swap(v, ++last, i);
    swap(v, left, last);            // 交换left, last,如此v[last]固定位置
    qsort(v, left, last-1);
    qsort(v, last+1, right);
}

void swap(int *v[], int i, int j)
{
    char *temp;
    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

 

5.7 多维数组【日期转第几天】

日期转某年中第几天、某年第几天转某月某日:day_of_year, month_day

#include <stdio.h>

static char daytab[2][13] =
{
    {0,31,28,31,30,31,30,31,31,30,31,30,31},
    {0,31,29,31,30,31,30,31,31,30,31,30,31}
};
/* 日期转某年中第几天 */
int day_of_year(int year, int month, int day)
{
    int i, leap;
    leap = (year%4==0 && year%100!=0) || year%400==0; //判断闰年
    for(i=1; i<month; i++)
        day += daytab[leap][i];
    return day;
}
/* 某年第几天转某月某日 */
void month_day(int year, int yearday, int *pmonth, int *pday)
{
    int i, leap;
    leap = (year%4==0 && year%100!=0) || year%400==0;
    for(i=1; yearday>daytab[leap][i]; i++)
        yearday -= daytab[leap][i];
    *pmonth = i;            // 传指针是为了直接改变
    *pday = yearday;
}

int main()
{
    int year, month, day;
    printf("请输入年月日: ");
    scanf("%d %d %d", &year, &month, &day);
    printf("这天是该年的第%d天\n", day_of_year(year, month, day));
    return 0;
}

 

5.8 指针数组的初始化

static char *name[] = 
{
  "Illegal month",
  "January","February","March","April","May","June","July","August","September","October","November","December"
};

 

5.9 指针与多维数组

int a[10][20]; //二维数组,分配了200个 int 类型长度的存储空间
int *b[10]; //指针数组,每行长度可以不同

 

5.10 命令行参数

  • argc:参数计数,表示命令行参数的个数。
  • *argv[]:参数向量,指向字符串数组的指针。

argc 至少为1,argv[0] 值为启动该程序的程序名。

argv[argc] 的值必须为一个指针。

#include <stdio.h>

/* 回显程序命令行参数 版本1 */
int main(int argc, char *argv[])
{
   int i;
   for(i=1; i<argc; i++)
   {
       printf("%s%s", argv[i], (i<argc-1)? " " : "");
   }
   printf("\n");
   return 0;
}

#include <stdio.h>

/* 回显程序命令行参数 版本2 */
int main(int argc, char *argv[])
{
    while(--argc > 0)
       printf("%s%s", *++argv, (argc > 1)? " " : "");
   printf("\n");
   return 0;
}

参数命令行程序的运行方案:

  1. 找到可执行文件 .exe 的位置,复制路径(建议放在桌面)
  2. 打开cmd,输入: cd C:\Users\Administrator\Desktop\000temp_test\bin\Debug
  3. 000temp_test.exe hello, world
  4. 输出: hello, world

打印与第一个参数指定的模式匹配的行

#include <stdio.h>
#include <string.h>
#define MAXLINE 1000

int getLine(char *line, int max);

int main(int argc, char *argv[])
{
    char line[MAXLINE];
    int found = 0;
    if(argc != 2)
        printf("Usage: find pattern\n");
    else
        while(getLine(line, MAXLINE) > 0)
        {
            if(strstr(line, argv[1] != NULL))
            {
                printf("%s", line);
                found++;
            }
        }
    return found;
}

/* 将行保存到 s 中,并返回该行的长度*/
int getLine(char *s, int lim)
{
    int c, i;
    i = 0;
    while(--lim > 0 && (c=getchar()) != EOF && c != '\n')
       s[i++] = c;
    if(c == '\n')
        s[i++] = '\n';
    s[i] = '\0';
    return i;
}

标准库函数 strstr(s, t) 返回一个指针,指向字符串 t 在字符串 s 中第一次出现的位置。声明在头文件<string.h>中

 

5.11 指向函数的指针

int (*cmp)(void *, void *)

comp 是一个指向函数的指针。

具有两个 void* 类型的参数,返回值类型 int 。

 

5.12 复杂声明

int *f() // f 是一个函数,返回一个指向 int 型指针
int (*pf)() // pf是指向函数的指针,返回 int 对象
char **argv // 指向字符指针的指针
int (*daytab)[13] // 指向整型数组的指针(数组指针)
int *daytab[13] // daytab是一维数组,元素是指向 int 的指针(指针数组)
void *comp() // comp是函数,返回空指针
void (*comp)() // comp是指向函数的指针,返回空。
char (*(*x())[])() 
/*
(从内向外)
1.x是一个函数;
2.返回指针;
3.指针指向数组;
4.数组元素是指针;
5.指向函数;
6.返回 char。
*/
char (*(*x[3])())[5] 
/*
(从内向外)
1.x是拥有3个元素的数组;
2.数组元素是指针;
3.指向函数;
4.返回指针;
5.指针指向5个元素的数组;
6.数组元素是 char

2-dim 数组元素,五种引用方式:a[3][4]

  • a[i][j]
  • *(a[i]+j)
  • *(*(a+i)+j)
  • (*(a+i))[j]
  • *(&a[0][0]+4*i+j)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值