Q:

Why does posix_spawn() fail where popen() works?

I'm successfully using popen() to run commands from within my C program. As I understand, it uses fork() and exec() (or variants of those) behind the curtains. This works very well:
FILE *fd = popen("xterm", "r");
pclose(fd);

... will bring up a new xterm window, as expected.
Now I'm trying to achieve the same with posix_spawn(), which I understand to be possibly more resource-friendly, especially if we don't plan on communicating with the new child process:
/* p.we_wordv contains the argv, index 0 holds the actual command */
pid_t pid;
posix_spawnp(&pid, p.we_wordv[0], NULL, NULL, p.we_wordv, NULL);

... but this, for xterm as the command, yields the following on the parent's output:
xterm: Xt error: Can't open display:
xterm: DISPLAY is not set

Trying to launch other processes will yield other error messages, fail silently, or, in some cases like ls, work as expected. This makes it a bit hard for me to actually see a pattern yet.
Can you point out what is causing the second approach to behave differently than the first?

A:

The message DISPLAY is not set tells you that xterm didn't find the DISPLAY environment variable. All graphical-output programs use this environment variable to connect to your screen.
It didn't find the variable because the environment was empty (it's the last NULL in your posix_spawnp function call). It seems that popen reuses the environment of current process, so it doesn't have this problem.
You might want to pass a manually-created environment, containing only the needed stuff, or just pass whatever environment your process has. The latter is more flexible (xterm will inherit various configuration settings from your process, which inherits them from your shell) but may be a security risk.
To access the environment of your process, use the environ global variable or change your main function to receive an additional parameter:
int main(int argc, char *argv[], char *envp[])
{
    ...
    posix_spawnp(&pid, p.we_wordv[0], NULL, NULL, p.we_wordv, envp);
}

