C和指针的复习系列一:第一章~第七章

第一章:快速上手

1. 输入下标索引,输出内容

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

//读取索引序列
void read_column_numbers( int *columns, int len );
//打印字符串
void rearrange( char *intput, int *columns );

int main( void )
{
	int		columns[ 10 ];
	char		input[ 100 ];
	read_column_numbers( columns, 10 );

	while ( NULL != gets( input ) ){
		printf("original input:%s\n", input );
		rearrange( input, columns );
	}

	return 0;
}

void read_column_numbers( int *columns, int len )
{
	int			*end_columns = columns + len;
	while ( columns < end_columns - 1 && scanf("%d", columns) && *columns >= 0 ){
		columns++;
	}
	*columns = -1;
}

void rearrange( char *input, int *columns )
{
	int begin = 0;
	int end = 0;
	while ( ( -1 != *columns ) && -1 != *( columns + 1 ) ){
		begin = *columns;
		end = *( columns + 1 );
		if ( begin <= end ){
			while ( begin <= end ){
				printf("%c", *( input + begin ) );
				begin++;
			}
		}
		else{	//逆序显示
			while ( end <= begin ){
				printf("%c", *( input + begin ) );
				begin--;
			}
		}

		columns += 2;
	}
}

程序输出:


学会用指针.如果学习C语言不用指针,那编程将会失去乐趣.


第三章:数据

1. 特殊的字符串和字符数组

#include <stdio.h>
#include <string.h>

#define SIZE 12
int main( void )
{
	int	a = 12;
	char	*str = "";
	char	arr[ SIZE ];
	int	*pa = &a;
	int	i = 0;

	memset( arr, 0, sizeof( char ) * SIZE );		//为变量初始化是一个好习惯
	for ( i = 0; i < SIZE - 1; i++ ){
		arr[ i ] = 'a' + i;
	}
	arr[ SIZE - 1 ] = '\0';		//如果想正确输出,还是在末尾加入'\0'
	str = "hello world";		//但是字符串就不必了,因为默认会加入'\0'

	printf("the int a is:\n");
	printf("%d---0x%x\n", *pa, pa );
	printf("the string is:\n");
	printf("%s---0x%x\n", str, &str );
	printf("the char array is:\n");
	printf("%s---0x%x\n", arr, &arr );

	return 0;
}
程序输出:



2. 有趣的const常量

int const *pci;

这是一个指向整型常量的指针.我们可以修改指针的值,但不能修改它所指向的值,或者换种说法:const修饰的是int,而不是*pci,而int则代表的是其指针所指向的值的类型.如下面代码所示:

#include <stdio.h>

int main( void )
{
	int		a = 12;
	int		b = 14;
	int		const	*p = &a;

	p = &b;		//修改指针的值--right(这里指针的值是地址,而指针所指向的值才是我们通常意义上认为的值)
	printf ( "%d\n", *p );
	*p = b;		//修改指针所指向的值---error
	printf("%d\n", *p );

	return 0;
}
而:

int * const cpi;

则声明cpi为一个指向整型的常量指针.此时指针是常量,它的值是无法修改的,但是我们可以修改它所指向的整型的值,或者说:const修饰的是int *.

#include <stdio.h>

int main( void )
{
	int		a = 12;
	int		b = 14;
	int		* const	p = &a;

	p = &b;		//修改指针的值--error
	printf ( "%d\n", *p );
	*p = b;		//修改指针所指向的值---right
	printf("%d\n", *p );

	return 0;
}

但#define也可以创建常量.在C语言中,推荐使用#define,但是在C++中,推荐使用const.用#define还有一个好处是:只要允许使用字面值常量的地方都可以使用#define,比如创建数组.

第五章:操作符和表达式

1. 位操作符

例1:将指定的位设置为1

value = value | 1 << bitNumber;
例2:将指定的位清零

value = value & ~( 1 << bitNumber );
例3:对指定的位进行测试

value & 1 << bitNumber;

2. 左值和右值

左值就是那些能够出现在赋值符号左边的东西.右值就是那些可以出现在赋值符号右边的东西.左值有个要求是:必须表示一个特定的位置(即不能被修改).所以字符串常量不能是左值,因为它在内存中的位置是随机的.

第六章:指针

1. 一些基本的概念

1) 内存和地址

1---内存中的每个位置由独一无二的地址标识

2---内存中的每个位置都包含一个值

2) 值和类型

我们不能通过检查一个值的位来判断它的类型.我们要看这个值如何被解释.

3) NULL指针

NULL作为一个特殊的指针变量,表示不指向任何东西.要使一个指针变量为NULL,给它赋值0即可.

4) 指针常量

假定变量a存储于位置100,则:

*100 = 25;
的意思是把25赋值给a吗?实际上这条语句是非法的,因为字面值100的类型是整型,而间接访问操作符只能作用于指针类型表达式.如果确定要把25存储于位置100,则必须进行强制类型转换:

*( int * ) 100 = 25;

2. 一些实例

1). 在字符串数组中查找单个字符

#include <stdio.h>

//判断value是否在str中,存在返回1,否则返回0
int		find_char( char **str, char value );

int main( void )
{
	char *str[ 3 ] = { "hello", "world", "python" };
	char **strings = str;

	if ( find_char( strings, 't' ) ){
		printf ( "we find it\n" );
	}
	else{
		printf ( "we do not find it\n" );
	}

	return 0;
}

int		find_char( char **str, char value )
{
	while ( NULL != *str++ ){
		while ( '\0' != *( *str )++ ){		//注意,这里的++是针对*str,而不是str
			if ( value == **str ){
				return 1;
			}
		}
	}

	return 0;
}

程序输出:



2). 清除数组中的所有元素

正向:

#define N_VALUES 5
float	values[N_VALUES];
float	*vp = NULL;

for ( vp = &values[0]; vp < &values[N_VALUES]; ){
	*vp++ = 0;
}

反向:

#define N_VALUES 5
float	values[N_VALUES];
float	*vp = NULL;

for ( vp = &values[N_VALUES]; vp > &values[0]; ){
	*--vp = 0;
}

错误的反向:

#define N_VALUES 5
float	values[N_VALUES];
float	*vp = NULL;

for ( vp = &values[N_VALUES - 1]; vp >= &values[0]; vp-- ){
	*vp = 0;
}

因为:标准允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不允许与指向数组第一个元素之前的那个内存位置的指针进行比较.


3) 查找字符串(若追求高效,则用KMP算法)

#include <stdio.h>

//在source中查找chars中的字符
char *find_char( char *source, char *chars );

int main( void )
{
	char *source = "abcdef";
	char *chars = "xrcqef";
	printf ( "%s\n", find_char( source, chars ) );

	return 0;
}

char *find_char( char *source, char *chars )
{
	char *temp = NULL;

	while ( '\0' != *chars ){
		temp = source;
		while ( '\0' != *source ){
			if ( *chars == *source ){
				return source;
			}
			source++;
		}
		source = temp;
		chars++;
	}

	return NULL;
}

程序输出:



4) 删除字符串中匹配的子字符串

#include <stdio.h>

//删除字符串中等于substr的子字符串
int del_substr( char *str, char *substr );

int main( void )
{
	char		str[] = "abcdefghijk";		//字符数组可以被修改
	char		*substr1 = "cde";			//字符串不能被修改
	char		*substr2 = "ghk";

	del_substr( str, substr1 );
	printf ( "%s\n", str );
	del_substr( str, substr2 );
	printf ( "%s\n", str );

	return 0;
}

int del_substr( char *str, char *substr )
{
	char		*delBegin = NULL;		//用来保存被删除字符串的起始位置
	char		*tempSubstr = NULL;
	while ( '\0' != *str ){
		tempSubstr = substr;
		//找到匹配的首字母的位置
		while ( '\0' != *str && *substr != *str ){
			str++;
		}
		if ( '\0' == *str ){		//要进行适当的判断,否则可能造成意想不到的BUG
			break;
		}
		delBegin = str;
		while ( '\0' != *str && *substr == *str ){
			substr++;
			str++;
		}
		if ( '\0' == *substr ){		//匹配成功
			while ( *delBegin++ = *str++ ){
				;
			}
			return 1;
		}
		else{		//继续进行匹配
//			substr = '\0';
			substr = tempSubstr;
			str++;
		}
	}

	return 0;
}

程序输出:



5) 反转字符串

#include <stdio.h>

void reverse( char *str );

int main( void )
{
	char	str[] = "hello world";

	reverse( str );
	printf("%s\n", str );

	return 0;
}

void reverse( char *str )
{
	char	*begin = str;

	while ( '\0' != *str ){
		str++;
	}
	str--;
	while ( begin < str ){
		char temp = *begin;
		*begin++ = *str;
		*str-- = temp;
	}
}
程序输出:



6) 寻找素数

#include <stdio.h>
#include <string.h>

#define SIZE	1000

//判断是否为素数
void isPrime( int *arr );
//打印数组
void showArr( int *arr );

int main( void )
{
	int		arr[ SIZE ];
	memset( arr, 0, sizeof( int ) * SIZE );

	isPrime( arr );
	showArr( arr );

	return 0;
}

void isPrime( int *arr )
{
	int		i = 0;
	int		j = 0;
	for ( ; i < SIZE; i++ ){
		*( arr + 1 ) = 0;
		if ( ( ( 2 != i ) && !( i % 2 ) ) || ( ( 3 != i ) && !( i % 3 ) ) ){
			*( arr + i ) = 0;
		}
		else{
			*( arr + i ) = 1;
		}
	}

	for ( i = 4; i < SIZE; i++ ){
		if ( !( arr + i ) ){
			continue;
		}
		for ( j = 2 * i; j < SIZE; j += i ){
			arr[ j ] = 0;
		}
	}
}

void showArr( int *arr )
{
	int		i = 0;
	int		count = 0;
	for ( ; i < SIZE; i++ ){
		if ( *( arr + i ) ){
			printf("%3d ", i);
			count++;
		}
		if ( 0 ==  ( count + 1 ) % 10 ){
			printf("\n");
			count++;
		}
	}
	printf("\n");
}

程序输出:



第七章:函数

1. 定义分配内存空间,但是声明则只是向编译器说明:嗯,有这样一个东西.

2. 简单理解C语言中函数传递参数是传值而非传址:

#include <stdio.h>
#include <string.h>

void changeArr( int *arr1, int *arr2, int len );
int main( void )
{
	int		arr1[ 5 ];
	int		arr2[ 5 ];
	int		i = 0;

	memset( arr1, 0, sizeof( int ) * 5 );
	for ( i = 0; i < 5; i++ ){
		arr2[ i ] = 1;
	}
	changeArr( arr1, arr2, 5 );

	printf("the arr1 is:\n");
	for ( i = 0; i < 5; i++ ){
		printf("%d ", arr1[ i ] );
	}
	printf("\nthe arr2 is:\n");
	for ( i = 0; i < 5; i++ ){
		printf("%d ", arr2[ i ] );
	}
	printf("\n");

	return 0;
}

void changeArr( int *arr1, int *arr2, int len )
{
	int		i = 0;
	for ( i = 0; i < len; i++ ){
		arr1[ i ] = 1000 + i;
	}
	arr1 = arr2;		//为什么这里不起作用呢???
}
程序输出:



因为函数在传递数组的时候,实际上是传值的,这里的值是:数组的地址.所以arr1表示的地址实际上只是一个副本而已,修改副本并不会影响原先的值.

3) 简单的递归程序

1---输入数字,输出字符.如输入4267,输出'4','2','6','7'.

#include <stdio.h>

void binary_to_ascii( unsigned int value );

int main( void )
{
	binary_to_ascii( 4267 );
	printf("\n");

	return 0;
}

void binary_to_ascii( unsigned int value )
{
	unsigned int quotient;
	quotient = value / 10;
	if ( 0 != quotient ){
		binary_to_ascii( quotient );
	}
	printf ( "%c", value % 10 + '0' );
}

2---递归与迭代版本之计算菲波那契数

#include <stdio.h>

//递归版本
long fibonacci1( int n );
//迭代版本
long fibonacci2( int n );

int main( void )
{
	printf("%d\n", fibonacci1( 20 ) );
	printf("%d\n", fibonacci2( 20 ) );

	return 0;
}

long fibonacci1( int n )
{
	if ( n <= 2 ){
		return 1;
	}
	return fibonacci1( n - 1 ) + fibonacci1( n - 2 );
}

long fibonacci2( int n )
{
	int		result = 1;
	int		n_1 = 1;
	int		n_2 = 1;

	while ( n > 2 ){
		n -= 1;
		n_1 = n_2;
		n_2 = result;
		result = n_1 + n_2;
	}

	return result;
}

程序输出:


如果迭代够清晰,则用迭代,从效率方面考虑.


4) 可变参数列表

#include <stdio.h>
#include <stdarg.h>

float average( int n_values, ...);

int main( void )
{
	float		aveValue = 0.0;
	aveValue = average( 5, 1, 2, 3, 4, 5, 6 );		//这里第一个参数代表传递参数的个数,所以6在函数中并未被计算
	printf("%f\n", aveValue );

	return 0;
}

float average( int n_values, ...)
{
	va_list var_arg;
	int count;
	float sum = 0;

	va_start( var_arg, n_values );

	for ( count = 0; count < n_values; count++ ){
		sum += va_arg( var_arg, int );
	}

	va_end( var_arg );

	return sum / n_values;
}

程序输出:


1. 类型va_list配合va_start, va_arg, va_end使用

2. va_start初始化包含两个参数:第一个参数是va_list变量的名字。第二个参数是省略号前最后一个有名字的参数。va_start的初始化过程把var_arg变量设置为指向可变参数部分的第一个参数。

3. 为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list变量和参数列表中下一个参数的类型。

4. 最后,当访问完毕最后一个可变参数之后,我们需要调用va_end。


5) 公约数的递归求解

#include <stdio.h>

int gcd( int m, int n )
{

	if ( 0 == n || 0 == m % n ){
		return n;
	}
	else{
		return gcd( n, m % n );
	}
}

int main(void)
{
	int m = 23456;
	int n = 3456;
	printf("%d\n", gcd( m, n ));

	return 0;
}

程序输出:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值