C语言数组

数组(array)由一系列类型相同的元素构成。数组声明(array declaration)中包括数组元素的数目元素的类型

数组的初始化

1、在声明时对其进行初始化

使用花括号括起来的一些列数值来初始化数组,数值之间用逗号隔开,在数值和逗号之间可以使用空格符。
例如,用含有12个元素的数组可以用来存储12个月份的天数。
int days[MONTHS]={31,28,31,30,31,30,31,31,30,31,30,31};

2、为数组赋值

声明完数组后,可以借助数组的索引(即下标)对数组成员进行赋值(一般对于有规律的数组,可采用循环语句对元素逐个赋值)。C不支持吧数组作为一个整体来进行赋值,也 不支持用花括号括起来的列表形式进行赋值(初始化的时候除外)

3、注意点

1、数组初始化问题

当数值数目小于数组元素数目时,多余的数组元素被初始化为0。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中存储的是无用的数值(与普通变量相似,在初始化之前数组元素的数值是不定的。编译器使用的数值是存储单元中已有的数值,因此输出结果是不定的——C Primer Plus P246);但是如果部分初始化数组,未初始化的元素则被设置为0。

如果初始化列表中项目的个数大于数组大小,编译器会认为这是一个错误。为避免这样的错误,可以省略括号中的数字,让编译器自动匹配数组大小和初始化列表中的项目数目。

下面是一个比较经典的例子,摘自《C Primer Plus》

#include <stdio.h>

int main(void)
{
    const int days[]={31,28,31,30,31,30,31,31,30,31,30,31};
    int index;


    for(index=0;index<sizeof days/sizeof days[0];index++)
        printf("MONTH %d has %2d days.\n",index+1,days[index]);
    return 0;
}




C99中规定,在初始化列表中使用带有方括号的元素下标可以指定某个特定的元素:

int arr[6]={[5]=212};       //把arr[5]初始化为212,未经初始化的元素都将被设置为0

2、指定数组大小问题

在C99标准出现之前,声明数组时在方括号内只能使用整数常量表达式。整数常量表达式是由整数常量组成的表达式。sizeof表达式被认为是一个整数常量,而(和C++不一样)一个const值却不是整数常量。const修饰符修饰的变量不是常量,只是一个只读的变量,不允许修改。

数组中一些关键字的使用

const关键字:

有些时候需要使用只读数组,也就是程序从数组中读取数值,但是程序不向数组中写数据。在这种情况下声明并初始化数组时,建议使用关键字const。

指针和数组

数组标记实际上是一种变相使用指针的形式。一个变相使用的例子:数组名同时也是该数组首元素的地址。
C标准在描述数组时,确实借助了指针的概念。例如,在定义ar[n]时,意思是*(ar+n),即“寻址到内存中的ar,然后移动n个单元,再取出数值”。

字符数组

字符串的定义

定义字符串的方法很多,基本的办法是使用 字符串常量char数组char指针字符串数组。《C Primer Plus》一书中下面的例子基本涵盖了这几种字符串定义的方法。
#include <stdio.h>

#define MSG "You must have many talents. Tell me some."         //一个符号字符串常量
#define LIM 5
#define LINELEN 81

int main(void)
{

    char name[LINELEN];
    char talents[LINELEN];
    int i;
    const char m1[40]="Limit youself to one line's worth.";     //初始化一个大小已确定的char数组
    const char m2[]="If you can't think of anything, fake it."; //让编译器计算数组大小
    const char *m3="\nEnough about me-what's your name?";       //初始化一个指针
    const char *mytal[LIM]={"Adding numbers swiftly",
                    "Multiplying accurately","Stashing data",
                    "Following instructions to the letter",
                    "Understanding the C language"};            //初始化一个字符串指针数组

    printf("Hi!I'm Clyde the computer.""I have many talents.\n");
    printf("Let me tell you some of them.\n");
    puts("What were they? Ah, yes, here's a partial list.");
    for(i=0;i<LIM;i++)
    {
        puts(mytal[i]);
    }
    puts(m3);

    gets(name);
    printf("Well,%s,%s\n",name,MSG);
    printf("%s\n%s\n",m1,m2);
    gets(talents);
    puts("Let's see if I've got that list;");
    puts(talents);
    printf("Thanks for the information,%s.\n",name);
    return 0;
}

1、字符串常量

字符串常量(string constant),又称字符串文字(string literal),是指位于一对双引号中的任何字符。双引号里的字符加上编译器自动提供的结束标志\0字符,作为一个字符串被存储在内存里。程序中使用了了几个这样的字符串常量,大多数用作函数printf()和puts()的参数。注意,还可以用#define来定义字符串常量。
字符串常量属于静态存储(static storage)类。静态存储是指如果在一个函数中使用字符串常量,即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。整个引号中的内容作为指向该字符串存储位置的指针。这一点与数组名作为指向数组存储位置的指针类似。

2、字符串数组及其初始化

1、指定一个足够大的数组来容纳字符串:指定数组大小时,一定要确保数组元素比字符串长度至少多1(多出来的1个元素用于容纳空字符)。未被使用的元素均被自动初始化为0。这里的0是char形式的空字符,而不是数字字符0,其实也就是‘\0’。
const char m1[40]="Limit youself to one line's worth.";     //初始化一个大小已确定的char数组
注意在对数组只声明而不进行初始化时,必须指定数组的大小(因为编译器无法预先知道需要为它预留多大空间)。而且数组的大小必须为整型常量,而不能是运行时得到的变量值(事实上,在C99中可以使用变长数组,但仍然无法预先知道数组大小应为多大)。

2、让编译器决定数组大小:
const char m2[]="If you can't think of anything, fake it."; //让编译器计算数组大小
m2是一个指向给定字符串的指针。


3、使用指针符号建立字符串:
const char *m3="\nEnough about me-what's your name?";       //初始化一个指针
m3是一个指向给定字符串的指针。

总结:在只声明而不进行初始化时,必须指定字符串的大小;而在声明的同时完成初始化,也就是定义一个字符串数组时,可以不指定字符串的大小,这样有利于节省内存空间。

3、数组与指针

初始化一个存放字符串的字符数组
const char m3[]="\nEnough about me-what's your name?";       //初始化一个数组
可以使用数组符号:m3[i];也可以使用指针加法:*(m3+i);
数组的元素是变量(除非声明数组时带有关键字const),但是数组名不是变量。

初始化一个指向字符串的指针
const char *m3="\nEnough about me-what's your name?";       //初始化一个指针
可以使用数组符号:m3[i];也可以使用指针加法:*(m3+i);还可以使用增量运算符:*(m3++)。

<pre name="code" class="objc"><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">在数组形式中,m3是个地址常量。你不能更改m3,因为这意味着更改数组存储的位置(地址)。可以使用运算符m3+1来标识数组里下一个元素,但不允许使用++m3。增量运算符只能用在变量名前,而不能用在常量前。</span>
 
在指针形式中,程序开始执行时,还要为指针变量m3另外预留一个存储位置,以在该指针变量中存储字符串的地址。这个变量初始时指向字符串的第一个字符,但是它的值是可以改变的。因此,可以对它使用增量运算符。
总之,数组初始化是从静态存储区把一个字符串复制给数组,而指针初始化只是复制字符串的地址。

4、字符串数组

字符串数组可以使用下标来访问多个不同的字符串。
const char *mytal[LIM]={"Adding numbers swiftly",
                    "Multiplying accurately","Stashing data",
                    "Following instructions to the letter",
                    "Understanding the C language"};            //初始化一个字符串指针数组
使用:
for(i=0;i<LIM;i++)
    {
        puts(mytal[i]);
    }
因为LIM被宏定义为5,所以mytal是一个由5个指向char指针组成的数组。也就是说,mytal是个一维数组,而数组里的每一个元素都是一个char类型值得地址。第一个指针是mytal[0],它指向第一个字符串的第一个字符。
mytal数组实际上并不存放字符串,它只是存放字符串的地址(字符串存放在程序用来存放常量的那部分内存中)。可以把mytal[0]看作表示第一个字符串,*mytal[0]表示第一个字符串的第一个字符。由于数组符号和指针之间的关系,也可以用mytal[0][0]表示第一个字符串的第一个字符,尽管mytal并没有被定义成二维数组。

字符串输入

C库提供了三个读取字符串的函数:scanf()、gets()和fgets()。

1、创建存储空间

最简单的方法就是在声明中明确指出数组大小。
char name[81];

2、gets()函数

gets()(代表get string)函数从系统标准地输入设备(通常是键盘)获得一个字符串。因为字符串没有预定的长度,所以gets()需要知道输入何时结束。解决办法是读字符串直到遇到一个换行字符(\n),按回车键可以产生这个字符。它读取换行符之前(不包括换行符)的所有字符,在这些字符后添加一个空字符(’\0‘),然后把这个字符串交个调用它的程序。
用法:CodeBlocks中 char *__cdecl gets(char *_Buffer) __MINGW_ATTRIB_DEPRECATED_SEC_WARN;,函数返回一个指向char的指针值, gets()返回的指针与传递给它的是同一个指针。可见,参数为创建好空间的地址,例如上面数组名name刚好为一地址。 因此,可以将输入值送到某一指针,例如:ptr=gets(name);
gets()函数的构造如下:
char *gets(char *s)
{
    ......
    return(s);
}
getchar()只返回一个值而没有参数:
while((ch=getchar())!=EOF);

3、fgets()函数

gets()的一个不足是它不检查预留存储区是否能够容纳实际输入的数据,多出来的字符简单地溢出到相邻的内存区。fgets()函数改进了这个问题,它让您指定最大读入字符数。
由于fgets()是为文件I/O而设计的,在处理键盘输入时就不如gets()那么方便。fgets()和gets()有三方面不同:
1、它需要第二个参数来说明最大读入字符数。如果这个参数值为n,fgets()就会读取最多n-1个字符或者读完一个换行符为止,由这二者中最先满足的那个来结束输入。
2、如果fgets()读取到换行符,就会把它存到字符串里,而不是像gets()那样丢弃它。
3、它还需要第三个参数来说明读哪一个文件。从键盘上读数据时,可以使用stdin(代表standard input)作为该参数,这个标识符在stdio.h中定义。
eg:fgets(name,MAX,stdin);

4、scanf()函数

scanf()和gets()主要的差别在于它们如何决定字符串何时结束。scanf()更基于获取单词(get word)而不是获取字符串(get string)。
scanf()使用两种方法决定输入结束。无论哪种方法,字符串都是以遇到的第一个非空白字符开始。如果使用%s格式,字符串读到(但不包括)下一个空白字符(比如空格、制表符或者换行符)。
scanf()函数返回一个整数值,这个值是成功读取的项目数;或者当遇到文件结束时返回一个EOF。

总结:根据所需输入的特点,用gets()从键盘读取文本可能要更好,因为它更容易被使用、更快,而且更简洁。scanf()主要用于以某种标准形式输入的混合类型数据的读取和转换。

字符串输出

C有三个用于输出字符串的标准库函数:puts()、fputs()和printf()。

1、puts()函数

puts()函数的使用非常简单,只需要给出字符串参数的地址。与printf()不同,puts()显示字符串时自动在其后添加一个换行符。
puts()函数遇到空字符时它就会停下来,所以应该确保有空字符存在。

2、fputs()函数

fputs()函数时puts()的面向文件版本,两者之间的主要区别是:
1、fputs()需要第二个参数来说明要写的文件。可以使用stdout(代表standard output)作为参数来进行输出显示,stdout在stdio.h中定义。
2、与puts()不同,fputs()并不为输出自动添加换行符。

3、printf()函数

如同puts()一样,printf()需要一个字符串地址作为参数。printf()函数使用起来没有puts()那么方便,但是它可以格式化多种数据类型,因而更通用。
int main(void)
{
    int i;
    const char *head="I love Millie!";
    for(i=0;head[i]!='\0';i++)
    {
        putchar(head[i]);
    }
    printf("\n");

    for(i=0;*(head+i)!='\0';i++)
    {
        putchar(*(head+i));
    }
    printf("\n");


    puts(head);
    printf("%s\n",head);
    for(;*head!='\0';)<span style="white-space:pre">		</span>//当head指向空字符时,*head的值为0,这将结束循环。因此,直接写为for(;*head;)也可以
    {
        putchar(*(head++));
    }
    printf("\n");
}
上面这个例子不仅展示了puts()、putchar()和printf()的区别,也展示了使用指针符号建立的字符串的方法,有head[i]、*(head+i)。
其中,可以看出,puts()适合输出字符串,函数参数为要输出字符串的地址,输出完之后自动添加换行符。
putchar()函数适合每次输出一个字符
printf()函数可以输出带有其他字符格式的字符串,参数同样为要输出的字符串的地址,并且要在格式列表中指明要输出的格式,例如,这里要输出字符串,格式列表中就要在对应的位置用%s注明。

字符串函数

ANSIC C用头文件string.h给出这些函数的原型。下面是一些最有用和最常用的函数:strlen()、strcat()、strncat()、strcmp()、strncmp()、strcpy()和strncpy()。

1、strlen()函数:得到字符串的长度

字符串就是一串零个或多个字符,并且以一个位模式为全0的NUL字节结尾。NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度并不包括NUL字节。

2、strcat()函数

strcat()(代表string concatenation)函数将第二个字符串的一份拷贝添加到第一个字符串的结尾,从而使第一个字符串成为一个新的组合字符串,第二个字符串并没有改变。strcat()函数时char *(指向char的指针)类型。这个函数返回它的第一个参数的值,即其后添加了第二个字符串的那个字符串中第一个字符的地址。

3、strncat()函数

strcat()函数并不检查第一个数组是否能够容纳第二个字符串。如果没有为第一个数组分配足够大的空间,多出来的字符溢出到相邻存储单元时就会出现问题。
strncat()函数需要另一个参数来指明最多允许添加的字符的数目。例如:strncat(bugs,addon,13)函数把addon字符串中的内容添加到bugs上,直到加到13个字符或遇到空字符为止,由二者中先符合的那一个来终止过程(注意,添加13个字符不包括空字符,但是添加13个字符之后,将自动加上空字符)。

4、strcmp()函数


比较字符串内容(content)的函数,如果两个字符串参数相同,它就会返回0。

5、strncmp()函数

strcmp()函数比较字符串时,一直比较到找到不同的相应字符,搜索可能要进行到字符串结尾处。
而strncmp()函数比较字符串时,可以比较到字符串不同处,也可以比较完由第三个参数指定的字符数。

6、strcpy()和strncpy()函数

例:strcpy(qword[i],temp);第二个参数temp指向的字符串被复制到第一个参数qword[i]指向的数组中。复制的那份字符串被称为目标(target)字符串,最初的字符串被称为源(source)字符串。eg:strcpy(target,"Hi ho!");
总之,strcpy()接受两个字符串指针参数。指向最初字符串的第二个指针可以是一个已声明的指针、数组名或字符串常量。指向复制字符串的第一个指针应指向空间大到足够容纳该字符串的数据对象,比如一个数组。 记住,声明一个数组将为数据分配存储空间;而声明一个指针只为一个地址分配存储空间。
strcpy()和gets()函数同样都有一个问题,那就是都不检查目标字符串是否容纳得下源字符串。复制字符串使用strcpy()比较安全。它需要第三个参数来指明最大可复制的字符数。
strncpy(target,source,n)从source把n个字符(或空字符之前的字符,由二者中最先满足的那个决定何时终止)复制到target。因此,如果源字符串的字符数比n小,整个字符串被复制过来,包括空字符。函数复制的字符数绝不会超过n,因此如果源字符串还没有结束就达到了限制,就不会添加空字符。结果,最终的字符串可能有也可能没有空字符。出于这个原因,程序设置的n比目标数组的大小要少1,这样就可以把空字符放到数组的最后一个元素里。

7、sprintf()函数

char *strcpy(char *s1,const char *s2);
该函数把s2指向的字符串(包括空字符)复制到s1指向的位置,返回值是s1。
char *strncpy(char *s1,const char *s2,size_t n);
该函数把s2指向的字符串复制到s1指向的位置,复制的字符数不超过n个。返回值是s1。空字符后的字符不被复制。如果源字符串的字符数少于n个,在目标字符串中就以空字符填充。如果源字符串的字符数大于或等于n个,空字符就不被复制。返回值是s1。


char *strcat(char *s1,const char *s2);
s2指向的字符串被复制到s1指向字符串的结尾。复制过来的s2所指字符串的第一个字符覆盖了s1所指字符串结尾的空字符。返回值是s1。
char *strncat(char *s1,const char *s2,size_t n);
s2字符串中只有前n个字符被追加到s1字符串,复制过来的s2字符串的第一个字符覆盖了s1字符串结尾的空字符。s2字符串中的空字符及其后的任何字符都不会被复制,并且追加一个空字符到所得结果后面。返回值是s1。


int strcmp(const char *s1,const char *s2);
如果s1字符串在机器编码顺序中落后于s2字符串,函数返回值是一个整数;如果两个字符串相同,返回值是0;如果第一个字符串在机器编码顺序中先于第二个字符串,返回值是一个负数。
int strncmp(const char *s1,const char *s2,size_t n);
该函数的作用和strcmp()一样,只是比较n个字符后或遇见第一个空字符时会停止比较,由二者中最先被满足的那一个条件终止比较过程。


char *strchar(const char *s,int c);
该函数返回一个指向字符串s中存放字符c的第一个位置的指针(标志结束的空字符是字符串的一部分,应该也可以搜索到它)。如果没有找到该字符,函数就返回空指针。
char *strpbrk(const char *s1,const char *s2);
该函数返回一个指针,指向字符串s1中存放s2字符串中任何字符的第一个位置。如果没找到任何字符,函数就返回空指针。
char *strrchr(const char *s,int c);
该函数返回一个指针,指向字符串s中字符c最后一次出现的地方(标志结束的空字符是字符串的一部分,因此也可以搜索到它)。如果没找到该字符,函数就返回空指针。
char *strstr(const char *s1,const char *s2);
该函数返回一个指针,指向s1字符串中第一次出现s2字符串的地方。如果在s1中没找到s2字符串,函数就返回空指针。


size_t strlen(const char *s);
该函数返回s字符串中的字符个数,其中不包括标志结束的空字符。












把字符串转换为数字

对于数字运算(比如加法运算和比较运算)C要求数字形式。但是在屏幕上显示数字却要求字符串形式,这是因为屏幕显示的字符。



  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值