C’s write Function: Complete Guide, Usage & Troubleshooting

1. Introduction

C language is a powerful programming language widely used in system programming, embedded systems, and other areas. Among them, the write function is one of the essential functions for low-level I/O operations. This article provides a detailed explanation of the write function from basics to advanced usage, helping readers build practical programs.

2. write Function Basics

What is the write function?

write function is a C language system call used to write data via a file descriptor. By using this function, you can send data directly to standard output, files, etc.

write Function Definition

Below is the signature of the write function.
ssize_t write(int fd, const void *buf, size_t count);
  • Return value: The number of bytes actually written (returns -1 on error).
  • Parameter description:
  • fd (file descriptor): An integer indicating the write destination. You can also specify standard output (1) or standard error (2), etc.
  • buf (buffer): The memory address containing the data to be written.
  • count (number of bytes to write): The size of the data to write from the buffer.

Use Cases

  • Output data to standard output.
  • Save binary data or text to a file.
  • Low-level data manipulation in embedded systems or OS kernels.

Error Handling

If the write function returns an error, check errno to identify the cause. Below are examples of errors.
  • EACCES: No permission to write to the file.
  • EBADF: An invalid file descriptor was specified.
  • EFAULT: An invalid buffer address was specified.
Example code for handling errors:
if (write(fd, buf, count) == -1) {
    perror("write error");
}
侍エンジニア塾

3. write Function Usage Example

Writing a string to standard output

write function is used to display a string on standard output (the console) in this basic example.
#include <unistd.h>

int main() {
    const char *message = "Hello, World!\n";
    write(1, message, 14); // 1 indicates standard output
    return 0;
}
Key points:
  • 1 is the file descriptor for standard output.
  • The buffer size is set to 14 (the length of the string).
  • The output will be “Hello, World!”.

Writing data to a file

Next, here’s an example of using the write function to write data to a file.
#include <fcntl.h>
#include <unistd.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT, 0644);
    if (fd == -1) {
        perror("open error");
        return 1;
    }

    const char *data = "This is a test.\n";
    ssize_t bytes_written = write(fd, data, 16);
    if (bytes_written == -1) {
        perror("write error");
    } else {
        printf("Successfully wrote %zd bytes.\n", bytes_written);
    }

    close(fd);
    return 0;
}
Key points:
  • The open function opens the file, write writes data, and close closes the file.
  • O_WRONLY opens for write‑only, and O_CREAT creates the file if it does not exist.
  • Permission 0644 sets the owner to read/write and others to read‑only.

Writing binary data

The write function can also be used to write binary data directly.
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

int main() {
    int fd = open("binary.dat", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open error");
        return 1;
    }

    uint8_t buffer[4] = {0xDE, 0xAD, 0xBE, 0xEF}; // 4-byte binary data
    ssize_t bytes_written = write(fd, buffer, sizeof(buffer));
    if (bytes_written == -1) {
        perror("write error");
    } else {
        printf("Successfully wrote %zd bytes.\n", bytes_written);
    }

    close(fd);
    return 0;
}
Key points:
  • A uint8_t buffer is used to write 4 bytes of binary data.
  • Specifying O_TRUNC truncates existing data before writing.

Cautions

  • The size of the data to write (count) must be specified accurately. An inaccurate value can cause unintended data to be written.
  • Ensure that the buffer’s memory address is valid. Using an invalid address can cause a segmentation fault.

4. Differences between write and printf Functions

printf Function Features

printf function is used to output formatted data to standard output. The following outlines its features.
  1. Formatting Capability
  • printf can use format specifiers (e.g., %d, %s) to format and output numbers and strings.
  • Example: c int value = 42; printf("The answer is %d ", value); Output: The answer is 42
  1. High-Level Operation
  • printf is part of the standard library and uses the write function internally.
  • Output data is temporarily stored in a buffer and written at the appropriate time.
  1. Target Is Standard Output Only
  • The destination is limited to standard output; you cannot specify a file descriptor directly.

write Function Features

write function provides lower-level operations. The following are its features.
  1. No Formatting Capability
  • write has no formatting capability and outputs the specified data as‑is.
  • Example: c const char *message = "Hello, World! "; write(1, message, 14); Output: Hello, World!
  1. Low-Level Operation
  • Data is written immediately, with no buffering.
  • It does not depend on the standard library and calls the system call directly.
  1. Flexible Output Destinations
  • Because it uses file descriptors, you can write data to any file or device, not just standard output.

Differences in Buffering

A major difference between them is the method of data buffering.
  • printf function: Data is stored in an internal buffer of the standard library and written in bulk when conditions are met (e.g., on newline or when the buffer is full).
  • Benefit: Improves performance.
  • Drawback: Data may not appear until the buffer is flushed.
  • write function: No buffering; data is output immediately each time it is called.
  • Benefit: Guarantees immediate output.
  • Drawback: Frequent calls can degrade performance.

Guidelines for Choosing

ConditionRecommended FunctionReason
Formatted output neededprintfCan format data using format specifiers
Immediate output neededwriteWrites data instantly without buffering
Output to files or deviceswriteCan target any file descriptor
Performance‑focusedprintf (conditional)Efficiently buffers for standard output

Comparison with Examples

Using printf:
#include <stdio.h>

int main() {
    int value = 42;
    printf("Value: %d
", value);
    return 0;
}
Using write:
#include <unistd.h>

int main() {
    const char *message = "Value: 42
";
    write(1, message, 10);
    return 0;
}
Both produce the same result, but it is important to understand that their internal processing differs significantly.

5. Applications of the write function in file operations

Opening and Closing Files

To write data to a file, you first need to open the file. Use the open function to open the file, and when the write operation is complete, close the file with the close function. Basic code example:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("example.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open error");
        return 1;
    }
    close(fd);
    return 0;
}
Key points:
  • O_WRONLY: Opens the file in write‑only mode.
  • O_CREAT: Creates a new file if it does not exist.
  • O_TRUNC: Truncates the file to zero length if it already exists.
  • Third argument (0644): Sets the file’s access permissions.

Procedure for Writing Data to a File

The following shows a concrete example of writing using the write function. Code example:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("data.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open error");
        return 1;
    }

    const char *content = "Hello, File!
";
    ssize_t bytes_written = write(fd, content, 13);
    (bytes_written == -1) {
        perror("write error");
    } else {
        printf("Successfully wrote %zd bytes.
", bytes_written);
    }

    close(fd);
    return 0;
}
Key points:
  • write writes the specified string to the file.
  • Check the return value to see how many bytes were actually written.
  • If an error occurs, use perror to display the error message.

Error Handling and Considerations

When using the write function for file operations, errors can occur. Below are common errors and their remedies.
  1. Unable to open file (open error)
  • Cause: The file does not exist or insufficient access permissions.
  • Solution: Verify the correct path and appropriate permissions, and specify O_CREAT if needed.
  1. Write error (write error)
  • Cause: Insufficient disk space or file system issues.
  • Solution: Check the error code errno, log it, and identify the cause.
  1. Close error (close error)
  • Cause: Invalid file descriptor.
  • Solution: Ensure the file was opened correctly.
Example error‑handling code:
if (write(fd, content, 13) == -1) {
    perror("write error");
}

Practical Example of File Operations

An example of writing multiple lines of text to a file is shown. Code example:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int fd = open("multiline.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open error");
        return 1;
    }

    const char *lines[] = {"Line 1
", "Line 2
", "Line 3
"};
    for (int i = 0; i < 3; i++) {
        if (write(fd, lines[i], 7) == -1) {
            perror("write error");
            close(fd);
            return 1;
        }
    }

    close(fd);
    return 0;
}
Key points:
  • Use an array to write multiple lines of data.
  • Perform error checking inside the loop to ensure safety.

6. Troubleshooting

write function returns -1

Cause: write function returns -1, indicating an error. The following are possible causes.
  1. Invalid file descriptor
  • Reason: The file is not opened correctly, or it has already been closed.
  • Solution: Ensure that the file descriptor is valid. c if (fd < 0) { perror("Invalid file descriptor"); return 1; }
  1. Insufficient disk space
  • Reason: The storage on the device you are trying to write to is insufficient.
  • Solution: Check the disk space and ensure there is enough free space.
  1. Insufficient access permissions
  • Reason: The target file or directory does not have the required permissions.
  • Solution: Change the permissions of the file or directory. bash chmod u+w filename

Some data is not written

Cause: The write function does not guarantee that it will write the specified number of bytes (count). Especially when the file descriptor is a socket or pipe, data may be written only partially. Solution: Track the unwritten portion and handle it in a loop. Example:
#include <unistd.h>

ssize_t robust_write(int fd, const void *buf, size_t count) {
    ssize_t total_written = 0;
    const char *buffer = buf;

    while (count > 0) {
        ssize_t written = write(fd, buffer, count);
        if (written == -1) {
            perror("write error");
            return -1;
        }
        total_written += written;
        buffer += written;
        count -= written;
    }

    return total_written;
}

Segmentation fault occurs

Cause: If the buffer address passed to the write function is invalid, a segmentation fault occurs. Solution:
  • Ensure the buffer is properly initialized.
  • Verify that the pointer’s memory allocation is correct.
Example (incorrect code):
char *data;
write(1, data, 10); // data is not initialized
Corrected example:
char data[] = "Hello";
write(1, data, 5); // use initialized data

Write is interrupted

Cause: The write function may be interrupted by a signal. Solution: Check the error code EINTR and retry if necessary. Example:
#include <errno.h>
#include <unistd.h>

ssize_t retry_write(int fd, const void *buf, size_t count) {
    ssize_t result;
    do {
        result = write(fd, buf, count);
    } while (result == -1 && errno == EINTR);
    return result;
}

The written content results in unintended outcome

Cause:
  • The size of the buffer to write is incorrect.
  • The buffer contains unexpected data.
Solution:
  • Specify the correct size of the data to write.
  • Use debugging tools (e.g., gdb) to inspect the buffer contents.
gdb ./your_program

Troubleshooting summary

  • Check error codes
  • When an error occurs, use errno to identify the cause.
  • Example: if (write(fd, buf, size) == -1) { perror("write error"); }
  • Debugging methods
  • Use strace to trace system call behavior.
  • Output logs to pinpoint problem areas.

7. FAQ

Q1: write function when writing strings – what should you watch out for?

A: write function writes data for the specified number of bytes (count) and does not consider that a string is null‑terminated. Therefore, you need to specify the exact size of the data you want to write. Example (incorrect):
const char *message = "Hello, World!";
write(1, message, sizeof(message)); // mistakenly getting the size of the pointer
Corrected example:
const char *message = "Hello, World!";
write(1, message, strlen(message)); // specify the correct string length

Q2: How should you handle a negative return value from the write function?

A: If the return value is -1, an error occurred. You can check errno to determine the cause. Typical error codes are shown below.
  • EACCES: No permission to write to the file.
  • ENOSPC: No space left on device.
  • EINTR: Interrupted by a signal.
Example (error handling):
if (write(fd, buffer, size) == -1) {
    perror("write error");
    // log the error code as needed
}

Q3: What is the difference between the write function and the fwrite function?

A: Both write and fwrite are used to output data, but they differ as follows.
Featurewrite functionfwrite function
LevelLow‑level system callHigh‑level standard library function
BufferingNo bufferingBuffering provided by the standard library
How the output target is specifiedFile descriptorFILE * (stream)
Typical use casesFile systems and socketsFile operations (especially text processing)

Q4: How can you debug when using the write function?

A: You can efficiently debug write issues using the following methods.
  1. strace command
  • Trace the write system call to inspect the data passed and any errors.
  • Example: bash strace ./your_program
  1. Log output
  • Log the content and size of the data being written within the program.
  1. Use GDB (debugger)
  • Check the buffer contents at write time to verify the data is correct.

Q5: Why does writing to a file sometimes write less data than the intended size?

A: The amount of data the write function writes in a single call depends on the file descriptor and system state. For example, when using sockets or pipes, buffer size limits may cause only part of the data to be written. Solution: Track the unwritten portion and loop calling write.
ssize_t robust_write(int fd, const void *buf, size_t count) {
    size_t remaining = count;
    const char *ptr = buf;

    while (remaining > 0) {
        ssize_t written = write(fd, ptr, remaining);
        if (written == -1) {
            perror("write error");
            return -1;
        }
        remaining -= written;
        ptr += written;
    }

    return count;
}

Q6: Is the write function thread‑safe?

A: The write function is considered thread‑safe, but if multiple threads operate on the same file descriptor simultaneously, data may become interleaved. Solution:
  • Use synchronization mechanisms (e.g., mutexes) to prevent race conditions between threads.

8. Summary

In this article, we provided an in‑depth explanation of the C language write function, covering everything from basics to advanced topics, error handling, differences from printf, and troubleshooting. Below we recap the main points.

Importance of the write Function

  • The write function is a system call that enables low‑level data output and works with various destinations such as files, standard output, and sockets.
  • It lacks formatting capabilities, but it is extremely useful for immediate output and handling binary data.

Basic Usage

  • Signature and parameters of the write function:
  ssize_t write(int fd, const void *buf, size_t count);
  • fd: File descriptor that specifies the output destination.
  • buf: Buffer containing the data to be written.
  • count: Number of bytes to write.
  • We explored examples of writing to standard output, files, and binary data to learn its flexibility.

Differences from printf

  • The write call performs low‑level, direct output without buffering.
  • In contrast, printf provides formatting capabilities and enables higher‑level output operations.
  • Choosing between them based on the use case is important.

Error Handling and Debugging

  • When an error occurs in the write function, you can use errno to identify the cause.
  • We presented ways to handle typical errors such as invalid file descriptors, insufficient disk space, and lack of permissions.
  • Utilizing strace and debugging tools can streamline troubleshooting.

Troubleshooting and FAQ

  • We explained how to handle partial writes or interruptions and provided an example implementation for retrying.
  • The FAQ section comprehensively covered questions related to the write function.

Next Steps

  • Based on the knowledge of the write function gained in this article, combine it with other C system calls (e.g., read, lseek, close) to create practical programs.
  • Feel free to tackle more advanced applications such as file manipulation and socket communication.
As your understanding of the write function deepens, you can strengthen the fundamentals of system programming in C. I hope this article helps improve your programming skills. Thank you for reading!