Chapter 6: Comparison of Python and C
Moving from Python to C means learning where the two languages differ and where they are alike. This chapter covers the major differences in syntax, data types, and memory management, along with the common pitfalls, so that you can work with C effectively as a Python programmer.
6.1 Syntax differences
Python and C share many programming concepts, but their syntax is quite different.
6.1.1 Indentation vs. braces
Python:
if x > 0:
print("Positive")
else:
print("Non-positive")
C:
#include <stdio.h>
int main() {
int x = 10;
if (x > 0) {
printf("Positive\n");
} else {
printf("Non-positive\n");
}
return 0;
}
Explanation:
- Python marks blocks of code with indentation. C uses braces
{}.
6.1.2 Variable declarations
Python:
x = 10
y = 20.5
name = "Alice"
C:
#include <stdio.h>
int main() {
int x = 10;
float y = 20.5;
char name[] = "Alice";
return 0;
}
Explanation:
- Python uses dynamic typing, so variable types are inferred.
- C requires you to declare each variable explicitly, with its type.
6.2 Data type differences
Python has a variety of high-level data types. C’s types are more primitive, and C requires explicit memory management.
6.2.1 Lists vs. arrays
Python:
numbers = [1, 2, 3, 4, 5]
print(numbers[2]) # Output: 3
C:
#include <stdio.h>
int main() {
int numbers[] = {1, 2, 3, 4, 5};
printf("%d\n", numbers[2]); // Output: 3
return 0;
}
Explanation:
- Python lists can grow dynamically and hold elements of different types.
- C arrays have a fixed size and hold elements of the same type.
6.2.2 Strings
Python:
greeting = "Hello, World!"
print(greeting)
C:
#include <stdio.h>
int main() {
char greeting[] = "Hello, World!";
printf("%s\n", greeting);
return 0;
}
Explanation:
- Python strings are high-level objects.
- C strings are arrays of characters terminated by a null character (
\0).
6.3 Memory management
Python manages memory for you. In C you manage it manually, with functions such as malloc, calloc, realloc, and free.
6.3.1 Dynamic memory allocation
Python:
numbers = [1, 2, 3, 4, 5]
numbers.append(6)
print(numbers) # Output: [1, 2, 3, 4, 5, 6]
C:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *numbers = (int *)malloc(5 * sizeof(int));
if (numbers == NULL) {
printf("Memory allocation failed\n");
return 1;
}
for (int i = 0; i < 5; i++) {
numbers[i] = i + 1;
}
// Resize the array
int *resized = (int *)realloc(numbers, 6 * sizeof(int));
if (resized == NULL) {
printf("Memory reallocation failed\n");
free(numbers);
return 1;
}
numbers = resized;
numbers[5] = 6;
for (int i = 0; i < 6; i++) {
printf("%d ", numbers[i]);
}
free(numbers); // Free the allocated memory
return 0;
}
Explanation:
- Python manages the list’s memory automatically.
- In C, you allocate and deallocate the memory yourself.
- If
realloccan’t resize the block, it returnsNULLand leaves the original block allocated. That’s why the result goes into a separate pointer,resized. Writingnumbers = realloc(numbers, ...)would overwrite your only pointer to the old block withNULLon failure, and the block could never be freed.
6.4 Common pitfalls
Watch out for these common mistakes when you move from Python to C.
6.4.1 Buffer overflows
C gives you low-level access to memory, and if you aren’t careful, that can lead to buffer overflows.
Example of a buffer overflow:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10];
strcpy(buffer, "This is a very long string"); // Unsafe, causes buffer overflow
printf("Buffer: %s\n", buffer);
return 0;
}
Solution:
Use a safer function such as strncpy:
#include <stdio.h>
#include <string.h>
int main() {
char buffer[10];
strncpy(buffer, "This is a very long string", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // Ensure null-termination
printf("Buffer: %s\n", buffer);
return 0;
}
6.4.2 Dangling pointers
A dangling pointer points to a memory location that has been freed.
Example of a dangling pointer:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
*ptr = 10;
free(ptr);
// ptr is now a dangling pointer
// printf("Value after free: %d\n", *ptr); // This would lead to undefined behavior
return 0;
}
Solution:
Set the pointer to NULL after you free it:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
*ptr = 10;
free(ptr);
ptr = NULL; // Avoids dangling pointer
return 0;
}
6.4.3 Memory leaks
A memory leak occurs when allocated memory is not freed.
Example of a memory leak:
#include <stdlib.h>
void createLeak() {
int *ptr = (int *)malloc(10 * sizeof(int));
// Memory allocated is not freed
}
int main() {
createLeak();
return 0;
}
Solution: Make sure every block of memory you allocate is freed:
#include <stdlib.h>
void createLeak() {
int *ptr = (int *)malloc(10 * sizeof(int));
// Use the memory
free(ptr); // Free the allocated memory
}
int main() {
createLeak();
return 0;
}
6.5 Best practices in both languages
6.5.1 Code readability
In both languages, use meaningful variable names and write comments. Keep your indentation consistent in Python and your use of braces {} consistent in C.
6.5.2 Error handling
Python handles errors with exceptions. In C, you use return values and error codes.
Python:
try:
file = open('example.txt', 'r')
content = file.read()
print(content)
except IOError:
print("Error opening file")
C:
#include <stdio.h>
int main() {
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
printf("Error opening file\n");
return 1;
}
char buffer[256];
while (fgets(buffer, sizeof(buffer), file) != NULL) {
printf("%s", buffer);
}
fclose(file);
return 0;
}
6.5.3 Memory management
Python has automatic garbage collection. In C, you manage memory by hand with malloc/free.
Python:
data = [1, 2, 3, 4, 5]
# No need to free memory explicitly
C:
#include <stdlib.h>
int main() {
int *data = (int *)malloc(5 * sizeof(int));
if (data == NULL) {
return 1;
}
// Use the memory
free(data); // Free the allocated memory
return 0;
}