I think you are confusing "message" and "dest_addr".
Let's look at the prototype for sendto in expanded form:
ssize_t sendto (int sockfd,
const void *buf,
size_t length,
int flags,
const struct sockaddr *dest_addr,
socklen_t addrlen);
sockfd - this is the socket you created with a call to socket()
buf - this is a pointer to an ARRAY OF BYTES (i.e. they could have made buf of type char* ). That is, this is the data that you want to send across the wire encapsulated in a UDP packet.
length - this is how many bytes are in that array. If you didn't pass "length", it wouldn't know if "buf" was 1 byte or 10000 bytes.
flags - Typically 0. This is advanced stuff
dest_addr - this is a pointer to the destination address. Typically you initialize a sockaddr_in instance and cast its pointer value to a sockaddr* type.
addrlen - the size of the dest_addr. typically, sizeof(sockaddr_in). Address length is variable because dest_addr could pointer to an IPV4 address (sockaddr_in type) or an IPV6 address (sockaddr_in6 type), or some other type.
Example of sending a packet from local port 9999 to remote host "1.2.3.4" on its port 8888. Error checking of return codes left out for brevity.
int s;
sockaddr_in addrDest;
sockaddr_in addrLocal;
char* msg = "Hello World";
// create the socket
s = socket(AF_INET, SOCK_DGRAM, 0); // UDP socket
addrLocal.sin_family = AF_INET;
addrLocal.sin_port = htons(9999);
addrLocal.sin_addr = INADDR_ANY; // zero-init sin_addr to tell it to use all available adapters on the local host
// associate this socket with local UDP port 9999
result = bind(s, (struct sockaddr*)&addrLocal, 0);
// send "Hello world" from local port 9999 to the host at 1.2.3.4 on its port 8888
addrDest.sin_family = AF_INET;
addrDest.sin_port = htons(8888);
addrDest.sin_addr.s_addr = inet_addr("1.2.3.4");
// strlen(msg)+1 for terminating null char
result = sendto(s, msg, strlen(msg)+1, 0, (struct sockaddr*)&addrDest, sizeof(addrDest));