Learning C Language: Part Six

1. Character Arrays and Strings

#include <stdio.h>
int main(int argc, const char *argv[]) {
	// "abcde"
	// Multiple characters can be stored in a character array.
	char s1[5] = {'a', 'b', 'c', 'd', 'e'};
	int i = 0;
	for (i = 0; i < 5; i++) {
		printf("%c", s1[i]);
	}
	printf("\n");
	// "www.hqyj.com"
	// A string can also be directly stored in a character array.
	// char s2[12] = "www.hqyj.com"; // Length cannot be 12 because of the '\0'
	char s3[32] = {"www.hqyj.com"};
	char s4[32] = "www.hqyj.com"; // This method is commonly used.
	char s5[] = "www.baidu.com"; // This method is commonly used.
	printf("sizeof(s3) = %ld\n", sizeof(s3)); // 32
	printf("sizeof(s4) = %ld\n", sizeof(s4)); // 32
	printf("sizeof(s5) = %ld\n", sizeof(s5)); // 14
	// Strings can be output using %s
	printf("s3 = %s\n", s3);
	printf("s5 = %s\n", s5);
	// char s1[5] = {'a', 'b', 'c', 'd', 'e'};
	// If it is a character array, %s cannot be used for output.
	// Because %s will look for '\0' and continue searching until it finds it.
	// This may cause an array access out of bounds error: unpredictable.
	// printf("s1 = %s\n", s1);
	// The following usage is fine.
	// This is not fully initialized; the rest is automatically initialized to 0, which is '\0'.
	char s6[6] = {'a', 'b', 'c', 'd', 'e'};
	printf("s6 = %s\n", s6);
	return 0;
}

2. String Handling Functions

strlen strcpy strcat strcmp

2.1 strlen

Header File

#include <string.h>

Function Prototype

size_t strlen(const char *s);

Functionality

Calculates the length of a string, note that it does not include ‘\0’.

Parameters

The address of the string whose length is to be calculated.

Return Value

The length of the string.

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
	char s1[64] = "hello world";
	int length = strlen(s1);
	printf("length = %d\n", length); // 11
	printf("sizeof(s1) = %ld\n", sizeof(s1)); // 64
	// If it is a character array, do not use strlen to calculate the length.
	// char s2[5] = {'a', 'b', 'c', 'd', 'e'};
	// int length2 = strlen(s2);
	// printf("length2 = %d\n", length2); // Incorrect usage, result is unpredictable.
	// strlen calculates length and stops at '\0'.
	char s3[32] = "hell\0o world";
	int length3 = strlen(s3);
	printf("length3 = %d\n", length3); // 4
	printf("sizeof(s3) = %ld\n", sizeof(s3)); // 32
	return 0;
}

Exercise:

Implement the functionality of the strlen function yourself.

#include <stdio.h>
int main(int argc, const char *argv[]) {
	char s[64] = "hello world";
	int count = 0;
	int i = 0;
	while (s[i] != '\0') {
		count++;
		i++;
	}
	// At the end of the loop, count contains the actual number of characters.
	printf("count = %d\n", count);
	// Using for loop
	count = 0;
	for (i = 0; s[i] != '\0'; i++) {
		count++;
	}
	printf("count = %d\n", count);
	return 0;
}

2.2 strcpy

Header File

#include <string.h>

Function Prototype

char *strcpy(char *dest, const char *src);

Functionality

Copies src to dest, note that dest must be large enough.

Parameters

dest: Destination string

src: Source string

Return Value

Returns dest

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
	// char s1[5] = "abcd"; // Not large enough
	char s1[32] = "abcd";
	char s2[32] = "hello world";
	printf("s1 = %s\n", s1); // abcd
	printf("s2 = %s\n", s2); // hello world
	// When using strcpy, ensure the destination string is large enough.
	strcpy(s1, s2);
	printf("s1 = %s\n", s1); // hello world
	printf("s2 = %s\n", s2); // hello world
	// If a shorter string is copied to a longer one, the remaining characters in the longer string are still in the array.
	// However, they cannot be accessed through the string method anymore.
	// It seems that the '\0' of the shorter string has also been copied.
	char s3[32] = "hqyj";
	strcpy(s1, s3);
	printf("s1 = %s\n", s1); // hqyj
	printf("s3 = %s\n", s3); // hqyj
	printf("s1[6] = %c\n", s1[6]);
	return 0;
}

Exercise:

Implement the functionality of the strcpy function yourself.

#include <stdio.h>
int main(int argc, const char *argv[]) {
	char s1[32] = "hqyj";
	char s2[32] = "hello world";
	printf("s1 = %s\n", s1); // hqyj
	printf("s2 = %s\n", s2); // hello world
	printf("------------------------------\n");
	int i = 0;
	while (s2[i] != '\0') {
		 s1[i] = s2[i];
		 i++;
	}
	// Note: Remember to copy the '\0' of s2 to s1.
	// s1[i] = '\0';
	 s1[i] = s2[i];
	printf("s1 = %s\n", s1); // hello world
	printf("s2 = %s\n", s2); // hello world
	return 0;
}

2.3 strcat

Header File

#include <string.h>

Function Prototype

char *strcat(char *dest, const char *src);

Functionality

Appends src (including ‘\0’) to the end of dest, overwriting dest’s ‘\0’, note that dest must be large enough.

Parameters

dest: Destination string

src: Source string

Return Value

Returns dest

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
	char s1[32] = "abcd";
	char s2[32] = "1234";
	printf("s1 = %s\n", s1); // abcd
	printf("s2 = %s\n", s2); // 1234
	printf("--------------------------------\n");
	// Append s2 to the end of s1.
	strcat(s1, s2);
	printf("s1 = %s\n", s1); // abcd1234
	printf("s2 = %s\n", s2); // 1234
	return 0;
}

Exercise:

Implement the functionality of the strcat function yourself.

#include <stdio.h>
int main(int argc, const char *argv[]) {
	char s1[32] = "abcd";
	char s2[32] = "12345";
	printf("s1 = %s\n", s1); // abcd
	printf("s2 = %s\n", s2); // 12345
	printf("--------------------------\n");
	// First find the '\0' of s1.
	int i = 0;
	while (s1[i] != '\0') {
		i++;
	}
	// When the loop ends, i is the index of '\0' in s1.
	// Append one by one.
	int j = 0;
	while (s2[j] != '\0') {
		s1[i] = s2[j];
		i++;
		j++;
	}
	// Remember to append the '\0' of s2 as well.
	s1[i] = s2[j];
	printf("s1 = %s\n", s1); // abcd12345
	printf("s2 = %s\n", s2); // 12345
	return 0;
}

2.4 strcmp

Header File

#include <string.h>

Function Prototype

int strcmp(const char *s1, const char *s2);

Functionality

Compares s1 and s2.

Compares the ASCII values of characters in s1 and s2 one by one until the first unequal character appears or both reach ‘\0’.

Parameters

s1, s2: The two strings to be compared.

Return Value

0 —> s1 == s2

<0 —> s1 < s2

>0 —> s1 > s2

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
	char s1[32] = "azasdfasdfc";
	char s2[32] = "aac";
	// Compare the ASCII values of characters in the two strings one by one until a size relationship appears, then return immediately.
	// Or if the characters before the first '\0' in both strings are equal, return equal.
	// Note: The length of the strings is not compared.
	int ret = strcmp(s1, s2);
	if (0 == ret) {
		printf("s1 == s2\n");
	} else if (ret < 0) {
		printf("s1 < s2\n");
	} else if (ret > 0) {
		printf("s1 > s2\n");
	}
	// The actual return value is the difference in ASCII values of the first unequal character, s1 - s2.
	// Just for understanding.
	printf("ret = %d\n", ret);
	// The version with n indicates that only the first n characters are compared.
	ret = strncmp(s1, s2, 1);
	if (0 == ret) {
		printf("s1 == s2\n");
	} else if (ret < 0) {
		printf("s1 < s2\n");
	} else if (ret > 0) {
		printf("s1 > s2\n");
	}
	return 0;
}

3. Pointers

3.1 Concept

Every byte of space in memory has a number, which is the pointer, also called the address.

Variables that store addresses are called pointer variables.

However, in general, when we talk about

Pointer: We refer to pointer variables.

Address: We refer to address numbers.

3.2 Pointer-related Operations

&: Get the address of a variable.

*: When defining a pointer variable, it only serves as an identifier, indicating that a pointer variable is defined.

In any other context, using * on a pointer indicates that we want to operate on the content in the space pointed to by the pointer.

3.3 Relationship Diagram of Pointers and Variables

Learning C Language: Part Six

3.4 Usage and Precautions of Pointers

#include <stdio.h>
int main(int argc, const char *argv[]) {
	// When the program runs to the statement defining the variable,
	// The operating system allocates the corresponding size of memory space for the variable based on its type.
	int a = 10;
	// By using the & symbol, we can get the variable's address. Note that for multi-byte variables,
	// &variable name gets the address of the smallest numbered one.
	printf("&a = %p\n", &a);
	// In a 32-bit system, the maximum address value is 0xFFFFFFFF (4 bytes).
	// In a 64-bit system, the maximum address value is 0xFFFFFFFFFFFFFFFF (8 bytes).
	// Generally, do not use ordinary variables to store addresses.
	// It can be stored, but cannot operate on this address; ordinary variables cannot use *.
	// long value = &a;
	// printf("value = %p\n", (void *)value);
	// *value = 520;
	// Pointer variables can be used to store addresses.
	// Define a pointer variable named p of type int *.
	// The type of the pointer determines how many bytes can be operated starting from the address stored in the pointer.
	// int * can operate 4 bytes.
	// char * can operate 1 byte.
	// The pointer p stores the address of variable a.
	int *p = &a;
	// Using * on the pointer variable indicates that we are operating on the content in the space pointed to by the pointer.
	printf("p = %p    *p = %d\n", p, *p);
	// Since *p and a operate on the same memory space,
	// changing one will change the other.
	a = 520;
	printf("a = %d     *p = %d\n", a, *p);
	*p = 1314;
	printf("a = %d     *p = %d\n", a, *p);
	// The pointer p itself is also a variable and requires corresponding space in memory.
	// Note: In a 32-bit system, any type of pointer is 4 bytes.
	// In a 64-bit system, any type of pointer is 8 bytes.
	// Generally, the type of the pointer variable should match the type of the variable whose address it stores,
	// to ensure that the size of the space operated is the same, so as not to lose data.
	// When defining multiple pointers in one line, be careful.
	// int *p1, p2, p3; // In this case, p1 is a pointer, while p2 and p3 are int type variables.
	// int *p1, *p2, *p3; // In this case, all are pointers.
	// Pointers can only store addresses that have been allocated to you,
	// and cannot be arbitrarily specified, otherwise illegal access may occur, leading to segmentation faults.
	// int *p4 = 0x20;
	// printf("p4 = %p\n", p4); // Can be stored, but cannot use *.
	// *p4 = 100; // Segmentation fault.
	// If a pointer is defined without initialization, it contains a random value.
	int *p5; // The address stored in p5 is a random value.
	// This means that the pointer's direction is uncertain.
	// Such a pointer is called a wild pointer.
	// *p5 = 123; // Incorrect usage, error is unpredictable.
	// If you do not know who the pointer will point to later when defining it,
	// you can initialize the pointer with NULL.
	// NULL is essentially (void *)0.
	int *p6 = NULL;
	*p6 = 100; // This will definitely cause a segmentation fault.
	p6 = &a; // Let p6 point to a.
	return 0;
}

3.5 Pointer Arithmetic

Pointer arithmetic refers to operations between the addresses stored in pointer variables.

Therefore, the operations that can be performed are limited.

Common operations for pointer variables:

Arithmetic operations (+ – ++ –)

Relational operations (> < >= <= == !=)

Assignment operations (= += -=)

#include <stdio.h>
int main(int argc, const char *argv[]) {
	int s[5] = {10, 20, 30, 40, 50};
	int *p1 = &s[0];
	int *p2 = &s[2];
	// Subtracting two pointer variables.
	// The difference is the number of data types, p2 - p1 gives the number of int.
	printf("p2 - p1 = %ld\n", p2 - p1); // 2
	char *q1 = (char *)p1;
	char *q2 = (char *)p2;
	printf("q2 - q1 = %ld\n", q2 - q1); // 8
	if (p1 == q1) { // Warning due to different types, but values are the same.
		printf("p1 == q1\n");
	}
	// Adding or subtracting a number to a pointer variable
	// is equivalent to adding or subtracting the size of several pointer data types.
	int *p3 = NULL;
	p3 = p1 + 4;
	printf("*p3 = %d\n", *p3); // 50
	// Pointer variables can change their direction.
	int a = 1314;
	p3 = &a;
	printf("*p3 = %d\n", *p3); // 1314
	printf("---------------------------\n");
	// For ++ -- operations, pay attention to the following usage.
	// int s[5] = {10, 20, 30, 40, 50};
	int *p5 = &s[0];
	printf("p5 = %p\n", p5);
	printf("*++p5 = %d\n", *++p5); // 20
	printf("p5 = %p\n", p5);
	int *p6 = &s[0];
	printf("p6 = %p\n", p6);
	printf("*p6++ = %d\n", *p6++); // 10
	printf("p6 = %p\n", p6);
	// When writing code, do not make things difficult for yourself.
	// Change *++p5 to p5++; *p5.
	// Change *p6++ to *p6; p6++;
	return 0;
}

Exercise:

Define a normal variable a, of type int, with a value of 999.

Define a pointer variable p1, of type int *, to store the address of a;

Use p1 to change the value in a to 0x12345678.

Define a pointer variable p2, of type char *, also to store the address of a;

Use %#x to output the value of *p2 and *(p2 + 1).

#include <stdio.h>
int main(int argc, const char *argv[]) {
	int a = 999;
	int *p1 = &a;
	*p1 = 0x12345678;
	char *p2 = (char *)&a;
	printf("*p2 = %#x\n", *p2);
	printf("*(p2 + 1) = %#x\n", *(p2 + 1));
	return 0;
}

Endianness Storage Issue:

Please design a program to determine whether your host uses big-endian or little-endian storage.

#include <stdio.h>
int main() {
	int a = 0x12345678;
	char *p = (char *)&a;
	if (*p == 0x78) {
		printf("Little-endian\n");
	} else if (*p == 0x12) {
		printf("Big-endian\n");
	}
	return 0;
}

3.6 Size of Pointers

#include <stdio.h>
int main(int argc, const char *argv[]) {
	// In a 32-bit system, the size of pointers is 4 bytes.
	// In a 64-bit system, the size of pointers is 8 bytes.
	// The size of pointers is independent of the pointer type.
	printf("sizeof(char *) = %ld\n", sizeof(char *));
	printf("sizeof(short *) = %ld\n", sizeof(short *));
	printf("sizeof(int *) = %ld\n", sizeof(int *));
	printf("sizeof(long *) = %ld\n", sizeof(long *));
	printf("sizeof(long long *) = %ld\n", sizeof(long long *));
	printf("sizeof(float *) = %ld\n", sizeof(float *));
	printf("sizeof(double *) = %ld\n", sizeof(double *));
	return 0;
}

4. Pointers and One-Dimensional Arrays

#include <stdio.h>
int main(int argc, const char *argv[]) {
	int s[5] = {10, 20, 30, 40, 50};
	// Analyze the role of the address constant of the array name in one-dimensional arrays.
	// array_name[index] accesses array members in essence:
	// Using the address saved by the array name as a reference, offset by several data types to access members.
	// s[0] <==> *s <==> *(s + 0)
	// s[1] <==> *(s + 1)
	printf("s[0] = %d     *(s + 0) = %d\n", s[0], *(s + 0)); // 10
	printf("s[1] = %d     *(s + 1) = %d\n", s[1], *(s + 1)); // 20
	// s = s + 1; // This operation is incorrect because the array name is a constant.
	// You can also define a pointer to store the address of the one-dimensional array.
	// Both of the following methods are valid.
	int *p1 = s; // -- Commonly used method.
	int *p2 = &s[0];
	printf("s = %p\n", s);
	printf("p1 = %p\n", p1);
	printf("p2 = %p\n", p2);
	// But note, do not write this way.
	// This method, although the value is the same as p1 and p2, operates on different spaces,
	// Equivalent to increasing the dimension of the array.
	// Remember: Never take the address of the array name in any case.
	// int *p3 = &s;
	// printf("p3 = %p\n", p3);
	// When the pointer points to the first address of the one-dimensional array,
	// The following equivalences hold:
	// s[i] <==> *(s + i) <==> p1[i] <==> *(p1 + i)
	*(p1 + 2) = 1314;
	printf("s[2] = %d   p1[2] = %d\n", s[2], p1[2]); // 1314
	// The difference between s and p1:
	// s is an address constant, cannot be assigned, nor can it be incremented.
	// p1 is a pointer variable, can be assigned, and can be incremented.
	p1 = &s[3];
	printf("*p1 = %d\n", *p1); // 40
	p1++;
	printf("*p1 = %d\n", *p1); // 50
	printf("---------------------------\n");
	// Traversing the one-dimensional array.
	int i = 0;
	for (i = 0; i < 5; i++) {
		// printf("%d  ", s[i]);
		// printf("%d  ", *(s + i));
		// printf("%d  ", p1[i]);
		// printf("%d  ", *(p1 + i));
		printf("%d  ", *p1);
		p1++;
	}
	putchar(10);
	return 0;
}

Pointer Operation Exercises:

1. Use pointers to implement the functionality of the strlen function.

#include <stdio.h>
#include <string.h>
int main(int argc, const char *argv[]) {
	char s[32] = "hello world";
	char *p = s;
	// Note the following situation.
	printf("%ld\n", strlen(s)); // 11
	printf("%ld\n", sizeof(s)); // 32
	printf("%ld\n", strlen(p)); // 11
	printf("%ld\n", sizeof(p)); // 8, which is the size of a pointer.
	int count = 0;
	// while (*p != '\0') {
	while (*p) {
		count++;
		p++;
	}
	printf("count = %d\n", count);
	return 0;
}

2. Use pointers to implement the functionality of the strcpy function.

#include <stdio.h>
int main(int argc, const char *argv[]) {
	char s1[32] = "hello world";
	char s2[32] = "abcd";
	char *p1 = &s1[0];
	char *p2 = s2;
	while (*p2 != '\0') {
		*p1 = *p2;
		p1++;
		p2++;
	}
	*p1 = '\0';
	// If you want to use p1 to output the string, note that you should first let p1 point back to s1.
	// Because during the operation, the direction of p1 has changed.
	p1 = s1;
	printf("p1 = %s\n", p1);
	return 0;
}

3. Write a program:

Input a string from the terminal using gets() “%[^
]”.

Count and output the number of spaces in the string.

#include <stdio.h>
int main(int argc, const char *argv[]) {
	char s[32] = {0};
	gets(s);
	int count = 0;
	char *p = s;
	while (*p != '\0') {
		if (' ' == *p) {
			count++;
		}
		p++;
	}
	printf("count = %d\n", count);
	return 0;
}

Leave a Comment