Apple Reference, Articles, and Opinions

C to Swift - Article 2

Fundamental Types - Void, void*, and Swift Void


In both C++ and Swift, the Void has similar meaning and uses. In C/C++ when you express a function to return void, it returns an incomplete type which cannot reused as an object. If you return a type of void* it can be used and assigned to a variable or constant. The use of Void in Swift always returns an empty tuple. In that sense, Swift does return something whether implicitly or explicitly defining a function of type Void

Consider the example below when attempting to make use and sense of the void type and void* pointer in C/C++

#include <stdio.h>

void printHello() 
{
    printf("Hello!");
}

void printBye() 
{
    printf("Bye!");
}

int main()
{
    // auto hello = printHello(); __error: ‘void hello’ has incomplete type__
    // void* bye = printBye();  __void value not ignored as it ought to be__

    return 0;
}

However, if we change the above function declaration return void* instead of void, it returns a void pointer, which is an actual memory location that can be accessed and interpreted.

void* printHello() // Implicitly returns a void pointer to memory location
void* printBye()  // Implicitly returns a void pointer to memory location

In C, returning and interpreting a pointer to void can be very useful for interpreting some allocated memory. For example, std::malloc returns a void pointer that is then used by the caller.

In Swift, the keyword is just a type alias defined as such type alias Void = (). We can see in the example below that we get a warning but Swift, allows us to assign a variable to an empty tuple. Swift returns () if a function implies Void return type as in the printBye() function.

func printHello() -> Void {
	print("Hello!")
}

func printBye() {
	print("Bye!")
}

var hello = printHello() // Warning: Variable 'hello' inferred to have type 'Void', which may be unexpected
var bye = printBye() // Warning: Variable 'bye' inferred to have type '()', which may be unexpected

NULL, nullptr, and nil


In C++ NULL is an ambiguous macro that is set to a constant or set to nullptr. This leads to more verbose programming like in this example below where one must account for different pointer arguments, which would lead to the explicit definition of an interface that takes a nullptr as an argument

#include <cstddef>
#include <iostream>
 
void f(int*)
{
   std::cout << "Pointer to integer overload\n";
}
 
void f(double*)
{
   std::cout << "Pointer to double overload\n";
}
 
void f(std::nullptr_t)
{
   std::cout << "null pointer overload\n";
}
Int main() 
{
	// Code…
}

For the purposes of checking for end of file, or programming to an interface, it is good practice to use nullptr in C and C++. Moreover, C allows NULL to be defined as a void* further adding to the confusion and ambiguity. In Swift, nil is much less ambiguous and can be thought of explicitly as memory that has not been allocated. Through the use of Optionals, nil can be explicitly interpreted in the declaration of a constant or variable like so.

var thisPointer: String?
thisPointer = “Hello!" // Without this declaration, we get a compilation error from below saying that we got a nil value
print(thisPointer!) // Force unwrapping not good practice

nil is the default value of any uninitialized Optional regardless of the data type you define it to be.

As an aside, the C void* pointer sort of resembles the Any type in Swift. A similar declaration of the above Optional that instead uses the Any type would then have to be cast just as you would with a void* however Swift does a lot of the clean up for you by implicitly casting it’s assignment.

var thisPointer: Any?
thisPointer = “Hello!" // Implicitly converted to be interpreted as String
print(thisPointer!) // Force unwrapping not recommended

In C, you would have to go through and explicitly cast it to be interpreted as the type you want like so

void* thisPointer;
thisPointer = (char*)(“Hello!"); // Cast at assignment
printf((char*)(thisPointer)); // Cast during use of printf function

While you may use UnsafePointers in Swift in recent versions, this series of articles is intended to be a lessons learned from myself to you when moving over from C/C++ style to Swift style of programming and these differences made it better for me to understand the Swift syntax.

Other data types

While the majority of the data types are used and defined the same way in Swift, below is a tables that has the equivalent C/C++ type with Swift and their differences if any.

C/C++SwiftKey Differences
boolBoolIn C++ evaluates to false for any integer not equal to 0 whereas in Swift, you must provide this functionality as an extension on Bool and Int. Note: This type exits as a macro in C that evaluates only to 0 or 1 as false and true, respectively.
intIntIn Swift, an Int is explicitly a signed Integer and has a length of 32 or 63 by default based on the underlying platform architecture. Furthermore, you can specify the length by prepending U to make the value unsigned and appending a number 8, 16, 32, 64 to specify the length in bits. Example: UInt8 is an unsigned 8-bit integer. In C/C++ int is defined as at least 16 bits, but you can use the short, long or long long keywords to specify 16, 32, and 64 bit signed integers respectively. You can also use the unsigned keyword to specify a non negative integer type. Example unsigned short int specifies a 16-bit unsigned integer.
floatFloatBoth languages represent a 32-bit floating point value. C/C++ documentation explicitly states conformance with IEEE 754 standard
doubleDoubleBoth languages represent a 64-bit floating point value. C/C++ documentation explicitly states conformance with IEEE 754 standard
charCharacterBoth languages represent characters in multiple encoding formats such as UTF-8, UTF-16, and ASCII. There are some slight differences, but none worth mentioning here.
std::stringStringThe C language does not have a string data type and strings are typically used as char*. All 3 of these can be thought of as an array of characters, although C++ and Swift offer many modern string operations including substring, appending, prepending, iterators, etc.

Constants and Variables

Declarations of constants and variables in both languages are straightforward. Constants are values that cannot be changed where variables can be changed and take on different values.

There are significant differences in constants for either language as the const keyword is much more powerful in C++ and no such keyword exists in Swift. To create a constant in C++ you must predicate the definition with the const keyword like so

const int num = 4;

Constants in C/C++ must be initialized to some value. If you are using a pointer with const you must pay attention to whether the const keyword is on the left or right hand side of the dereference * If it is on the left hand side of the dereference operator, it may point to another value of type int, but you cannot change that value.

int i = 1;
int j = 4;
const int* ptr = &i;
*ptr = 3; // You are trying to modify the contents of i which was marked as constant and therefore you will get a compilation error
ptr = &j; // Allowed because the pointer itself is not constant

If the const keyword is on the right hand side, then the data the pointer points to may modify the contents. Using the same example, we see now that line 4 is no longer valid and line is!

int i = 1;
int j = 4;
int* const ptr = &i;
*ptr = 3; // Allowed to change the contents of the pointers pointee
ptr = &j; // Error, pointer itself must be constant and therefore it cannot change what it points to

Finally if we use it on both the left and right hand side, you could imagine that the pointer itself nor the pointee’s contents may be changed.

In Swift declaring a constant is done through the let keyword and without pointers, we don’t really do much passed that.

let i: Int = 1 // i cannot be modified after this

Note that a constant may be initialized at runtime in Swift, but care should be taken to initialize the constant before using. Similarly, the runtime initialization in C++ would be done via the constructor.

Variables in both languages have simple declarations seen below where Swift and C++ both offer type inference. C++

auto inferredVariable = 4; // C++ infers the int data type
int explicitVariable = 4; // Explicit use of integer variable

Swift

var inferredVariable = 4 // Swift infers Int data type
var explicitVariable: Int = 4 // Explicit use of integer variable

That is it for my third article about programming in Swift for C developers. I hope you enjoyed it! If you have any comments or questions, or would like to inquire about freelance development help, please reach out via email or via Twitter

Thanks for reading!

With gratitude,
Jav Solo